aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.target58
-rw-r--r--audio/wavaudio.c2
-rw-r--r--audio/wavcapture.c2
-rw-r--r--block-vmdk.c1
-rw-r--r--block.c15
-rwxr-xr-xconfigure32
-rw-r--r--cpu-all.h12
-rw-r--r--cpu-exec.c19
-rw-r--r--cutils.c40
-rw-r--r--exec-all.h5
-rw-r--r--exec.c85
-rw-r--r--gdbstub.c149
-rw-r--r--hw/apic.c175
-rw-r--r--hw/boards.h2
-rw-r--r--hw/cirrus_vga.c173
-rw-r--r--hw/hw.h9
-rw-r--r--hw/hypercall.c362
-rw-r--r--hw/hypercall.h45
-rw-r--r--hw/i8254.c23
-rw-r--r--hw/i8259.c111
-rw-r--r--hw/ipf.c569
-rw-r--r--hw/ne2000.c6
-rw-r--r--hw/pc.c111
-rw-r--r--hw/rtl8139.c3
-rw-r--r--hw/usb-uhci.c4
-rw-r--r--hw/vga.c257
-rw-r--r--hw/vga_int.h27
-rw-r--r--hw/vmport.c28
-rw-r--r--ia64.ld2
-rw-r--r--ia64intrin.h150
-rw-r--r--kvm/.gitignore29
-rw-r--r--kvm/Makefile74
-rw-r--r--kvm/bios/.cvsignore1
-rw-r--r--kvm/bios/Makefile116
-rw-r--r--kvm/bios/Makefile.in117
-rw-r--r--kvm/bios/VGABIOS-lgpl-README214
-rw-r--r--kvm/bios/VGABIOS-lgpl-latestbin0 -> 38400 bytes
-rw-r--r--kvm/bios/VGABIOS-lgpl-latest-cirrusbin0 -> 35328 bytes
-rw-r--r--kvm/bios/VGABIOS-lgpl-latest-cirrus-debugbin0 -> 35840 bytes
-rw-r--r--kvm/bios/VGABIOS-lgpl-latest-debugbin0 -> 39936 bytes
-rwxr-xr-xkvm/bios/acpi-dsdt.dsl570
-rw-r--r--kvm/bios/apmbios.S365
-rw-r--r--kvm/bios/bios_usage4
-rw-r--r--kvm/bios/biossums.c504
-rwxr-xr-xkvm/bios/makesym.perl31
-rw-r--r--kvm/bios/notes44
-rw-r--r--kvm/bios/rombios.c10979
-rw-r--r--kvm/bios/rombios.h59
-rwxr-xr-xkvm/bios/rombios32.c1384
-rw-r--r--kvm/bios/rombios32.ld19
-rw-r--r--kvm/bios/rombios32start.S98
-rw-r--r--kvm/bios/usage.cc99
-rwxr-xr-xkvm/configure136
-rw-r--r--kvm/doxygen.conf1252
-rw-r--r--kvm/kernel/Kbuild8
-rw-r--r--kvm/kernel/Makefile82
-rw-r--r--kvm/kernel/anon_inodes.c221
-rw-r--r--kvm/kernel/debug.h23
-rw-r--r--kvm/kernel/external-module-compat.h654
-rw-r--r--kvm/kernel/hack-module.awk93
-rw-r--r--kvm/kernel/i386.config1783
-rw-r--r--kvm/kernel/include/asm-x86/cmpxchg.h5
-rw-r--r--kvm/kernel/include/linux/anon_inodes.h16
-rw-r--r--kvm/kernel/include/linux/magic.h41
-rw-r--r--kvm/kernel/include/linux/mutex.h5
-rw-r--r--kvm/kernel/kvm-kmod.spec50
-rw-r--r--kvm/kernel/preempt.c254
-rw-r--r--kvm/kernel/vmx-debug.c1078
-rw-r--r--kvm/kernel/x86_64.config1711
-rwxr-xr-xkvm/kvm267
-rw-r--r--kvm/kvm.spec134
-rwxr-xr-xkvm/kvm_stat89
-rw-r--r--kvm/libkvm/Makefile38
-rw-r--r--kvm/libkvm/config-i386.mak6
-rw-r--r--kvm/libkvm/config-ia64.mak5
-rw-r--r--kvm/libkvm/config-x86_64.mak6
-rw-r--r--kvm/libkvm/kvm-common.h86
-rw-r--r--kvm/libkvm/kvm-ia64.h28
-rw-r--r--kvm/libkvm/kvm-x86.h28
-rw-r--r--kvm/libkvm/libkvm-ia64.c94
-rw-r--r--kvm/libkvm/libkvm-x86.c504
-rw-r--r--kvm/libkvm/libkvm.c1031
-rw-r--r--kvm/libkvm/libkvm.h537
-rw-r--r--kvm/scripts/65-kvm.rules1
-rwxr-xr-xkvm/scripts/kvm226
-rwxr-xr-xkvm/scripts/mkbootdisk30
-rwxr-xr-xkvm/scripts/qemu-ifup5
-rwxr-xr-xkvm/scripts/run_img4
-rw-r--r--kvm/user/COPYRIGHT4
-rw-r--r--kvm/user/Makefile48
-rwxr-xr-xkvm/user/balloon_ctl.c92
-rw-r--r--kvm/user/bootstrap.lds15
-rw-r--r--kvm/user/config-i386.mak11
-rw-r--r--kvm/user/config-x86-common.mak45
-rw-r--r--kvm/user/config-x86_64.mak14
-rwxr-xr-xkvm/user/configure66
-rw-r--r--kvm/user/flat.lds17
-rw-r--r--kvm/user/main.c666
-rw-r--r--kvm/user/test/x86/access.c580
-rw-r--r--kvm/user/test/x86/bootstrap.S137
-rw-r--r--kvm/user/test/x86/cstart.S10
-rw-r--r--kvm/user/test/x86/cstart64.S168
-rw-r--r--kvm/user/test/x86/emulator.c157
-rw-r--r--kvm/user/test/x86/exit.c7
-rw-r--r--kvm/user/test/x86/hypercall.c19
-rw-r--r--kvm/user/test/x86/ioram.h7
-rw-r--r--kvm/user/test/x86/irq.S118
-rw-r--r--kvm/user/test/x86/lib/apic.h14
-rw-r--r--kvm/user/test/x86/lib/exit.c5
-rw-r--r--kvm/user/test/x86/lib/printf.c171
-rw-r--r--kvm/user/test/x86/lib/printf.h2
-rw-r--r--kvm/user/test/x86/lib/smp.c151
-rw-r--r--kvm/user/test/x86/lib/smp.h16
-rw-r--r--kvm/user/test/x86/lib/string.c21
-rw-r--r--kvm/user/test/x86/lib/string.h7
-rw-r--r--kvm/user/test/x86/memtest1.S44
-rw-r--r--kvm/user/test/x86/port80.c13
-rw-r--r--kvm/user/test/x86/print.S31
-rw-r--r--kvm/user/test/x86/print.h19
-rw-r--r--kvm/user/test/x86/runtime.h6
-rw-r--r--kvm/user/test/x86/sieve.c89
-rw-r--r--kvm/user/test/x86/simple.S13
-rw-r--r--kvm/user/test/x86/smptest.c32
-rw-r--r--kvm/user/test/x86/stringio.S31
-rw-r--r--kvm/user/test/x86/test32.S8
-rw-r--r--kvm/user/test/x86/vm.c268
-rw-r--r--kvm/user/test/x86/vm.h10
-rw-r--r--kvm/user/test/x86/vmexit.c32
-rw-r--r--migration.c1144
-rw-r--r--migration.h10
-rw-r--r--monitor.c31
-rw-r--r--osdep.c2
-rw-r--r--pc-bios/bios-vista.diff17
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/bios.diff57
-rw-r--r--pc-bios/bochs-manifest24
-rw-r--r--pc-bios/openbios-sparcbin0 -> 506966 bytes
-rw-r--r--pc-bios/vgabios-cirrus.binbin35328 -> 35328 bytes
-rw-r--r--pc-bios/vgabios.binbin37888 -> 38400 bytes
-rw-r--r--qemu-common.h2
-rw-r--r--qemu-kvm-helper.c40
-rw-r--r--qemu-kvm-ia64.c71
-rw-r--r--qemu-kvm-x86.c628
-rw-r--r--qemu-kvm.c807
-rw-r--r--qemu-kvm.h48
-rw-r--r--qemu_socket.h2
-rw-r--r--sdl.c16
-rw-r--r--sysemu.h6
-rw-r--r--target-i386/cpu.h19
-rw-r--r--target-i386/helper2.c12
-rw-r--r--target-ia64/cpu.h81
-rw-r--r--target-ia64/exec.h46
-rw-r--r--target-ia64/firmware.c579
-rw-r--r--target-ia64/firmware.h46
-rw-r--r--target-ia64/op.c22
-rw-r--r--target-ia64/op_helper.c109
-rw-r--r--target-ia64/translate.c39
-rw-r--r--vl.c690
-rw-r--r--vnc.c10
159 files changed, 35278 insertions, 159 deletions
diff --git a/Makefile.target b/Makefile.target
index 5cedc9895..12fb0434c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -1,5 +1,9 @@
+CFLAGS=
+LDFLAGS=
+
include config.mak
+LDFLAGS_BASE:=$(LDFLAGS)
TARGET_BASE_ARCH:=$(TARGET_ARCH)
ifeq ($(TARGET_ARCH), x86_64)
TARGET_BASE_ARCH:=i386
@@ -22,6 +26,9 @@ endif
ifeq ($(TARGET_ARCH), sparc64)
TARGET_BASE_ARCH:=sparc
endif
+ifeq ($(TARGET_ARCH), ia64)
+TARGET_BASE_ARCH:=ia64
+endif
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 -MP -DNEED_CPU_H
@@ -234,7 +241,7 @@ OP_LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS)
#########################################################
-CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D__user=
LIBS+=-lm
ifndef CONFIG_USER_ONLY
LIBS+=-lz
@@ -291,8 +298,8 @@ SRCS:= $(OBJS:.o=.c)
OBJS+= libqemu.a
# cpu emulator library
-LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\
- translate.o op.o host-utils.o
+LIBOBJS=exec.o kqemu.o qemu-kvm.o translate-op.o translate-all.o cpu-exec.o\
+ translate.o op.o host-utils.o qemu-kvm-helper.o
ifdef CONFIG_SOFTFLOAT
LIBOBJS+=fpu/softfloat.o
else
@@ -302,10 +309,12 @@ CPPFLAGS+=-I$(SRC_PATH)/fpu
ifeq ($(TARGET_ARCH), i386)
LIBOBJS+=helper.o helper2.o
+LIBOBJS+=qemu-kvm-x86.o
endif
ifeq ($(TARGET_ARCH), x86_64)
LIBOBJS+=helper.o helper2.o
+LIBOBJS+=qemu-kvm-x86.o
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
@@ -336,6 +345,11 @@ ifeq ($(TARGET_BASE_ARCH), alpha)
LIBOBJS+= op_helper.o helper.o alpha_palcode.o
endif
+ifeq ($(TARGET_BASE_ARCH), ia64)
+LIBOBJS+=op_helper.o firmware.o
+LIBOBJS+=qemu-kvm-ia64.o
+endif
+
ifeq ($(TARGET_BASE_ARCH), cris)
LIBOBJS+= op_helper.o helper.o
LIBOBJS+= cris-dis.o
@@ -345,6 +359,7 @@ LIBOBJS+= mmu.o
endif
endif
+
# NOTE: the disassembler code is only needed for debugging
LIBOBJS+=disas.o
ifeq ($(findstring i386, $(TARGET_ARCH) $(ARCH)),i386)
@@ -396,7 +411,7 @@ ifeq ($(ARCH),alpha)
endif
# must use static linking to avoid leaving stuff in virtual address space
-VL_OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o
+VL_OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o migration.o
# XXX: suppress QEMU_TOOL tests
ifdef CONFIG_WIN32
VL_OBJS+=block-raw-win32.o
@@ -419,6 +434,12 @@ ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o
endif
+ifdef CONFIG_KVM_KERNEL_INC
+CFLAGS += -I $(CONFIG_KVM_KERNEL_INC)
+LIBS += -lkvm
+DEPLIBS += ../libkvm/libkvm.a
+endif
+
ifdef CONFIG_VNC_TLS
CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
LIBS += $(CONFIG_VNC_TLS_LIBS)
@@ -439,6 +460,9 @@ VL_OBJS += ne2000.o
VL_OBJS += pcnet.o
VL_OBJS += rtl8139.o
+# PCI Hypercall
+VL_OBJS+= hypercall.o
+
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
@@ -447,6 +471,14 @@ VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
endif
+ifeq ($(TARGET_BASE_ARCH), ia64)
+# Hardware support
+VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
+VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o ipf.o
+VL_OBJS+= cirrus_vga.o parallel.o acpi.o piix_pci.o
+VL_OBJS+= usb-uhci.o smbus_eeprom.o
+CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
+endif
ifeq ($(TARGET_BASE_ARCH), ppc)
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
# shared objects
@@ -570,7 +602,7 @@ ifdef CONFIG_WIN32
SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
endif
-$(QEMU_SYSTEM): $(VL_OBJS) ../libqemu_common.a libqemu.a
+$(QEMU_SYSTEM): $(VL_OBJS) ../libqemu_common.a libqemu.a $(DEPLIBS)
$(CC) $(VL_LDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
depend: $(SRCS)
@@ -607,16 +639,30 @@ op.o: op.c
# variables
ifeq ($(TARGET_BASE_ARCH), i386)
# XXX: rename helper.c to op_helper.c
+qemu-kvm-x86.o: qemu-kvm-x86.c qemu-kvm.h
+
helper.o: helper.c
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
else
op_helper.o: op_helper.c
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
endif
+ifeq ($(TARGET_ARCH), ia64)
+qemu-kvm-ia64.o: qemu-kvm-ia64.c qemu-kvm.h
+firmware.o: firmware.c
+ $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+endif
cpu-exec.o: cpu-exec.c
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+qemu-kvm.o: qemu-kvm.c qemu-kvm.h
+ifeq ($(TARGET_BASE_ARCH), i386)
+qemu-kvm-helper.o: qemu-kvm-helper.c
+endif
+
+ $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+
# Note: this is a workaround. The real fix is to avoid compiling
# cpu_signal_handler() in cpu-exec.c.
signal.o: signal.c
@@ -634,7 +680,7 @@ clean:
install: all
ifneq ($(PROGS),)
- $(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
+ $(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
endif
ifneq ($(wildcard .depend),)
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 11ca86dda..2bdfdd41c 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -159,7 +159,7 @@ static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
- wav->f = qemu_fopen (conf.wav_path, "wb");
+ wav->f = qemu_fopen_file (conf.wav_path, "wb");
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
conf.wav_path, strerror (errno));
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index 3281c3110..cb072a192 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -134,7 +134,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
le_store (hdr + 28, freq << shift, 4);
le_store (hdr + 32, 1 << shift, 2);
- wav->f = qemu_fopen (path, "wb");
+ wav->f = qemu_fopen_file (path, "wb");
if (!wav->f) {
term_printf ("Failed to open wave file `%s'\nReason: %s\n",
path, strerror (errno));
diff --git a/block-vmdk.c b/block-vmdk.c
index af979a1e8..b5a48aa6e 100644
--- a/block-vmdk.c
+++ b/block-vmdk.c
@@ -93,7 +93,6 @@ typedef struct ActiveBDRVState{
static ActiveBDRVState activeBDRV;
-
static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
{
uint32_t magic;
diff --git a/block.c b/block.c
index 3cb3cc22d..24d78fc2e 100644
--- a/block.c
+++ b/block.c
@@ -868,6 +868,21 @@ void bdrv_flush(BlockDriverState *bs)
bdrv_flush(bs->backing_hd);
}
+void bdrv_iterate_writeable(void (*it)(BlockDriverState *bs))
+{
+ BlockDriverState *bs;
+
+ for (bs = bdrv_first; bs != NULL; bs = bs->next)
+ if (bs->drv && !bdrv_is_read_only(bs) &&
+ (!bdrv_is_removable(bs) || bdrv_is_inserted(bs)))
+ it(bs);
+}
+
+void bdrv_flush_all(void)
+{
+ bdrv_iterate_writeable(bdrv_flush);
+}
+
#ifndef QEMU_IMG
void bdrv_info(void)
{
diff --git a/configure b/configure
index b16fb84ee..c05dcc4a5 100755
--- a/configure
+++ b/configure
@@ -96,7 +96,9 @@ vnc_tls="yes"
bsd="no"
linux="no"
kqemu="no"
+kvm="no"
profiler="no"
+kernel_path=""
cocoa="no"
check_gfx="yes"
check_gcc="yes"
@@ -134,6 +136,7 @@ bsd="yes"
oss="yes"
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
kqemu="yes"
+ kvm="yes"
fi
;;
NetBSD)
@@ -190,6 +193,12 @@ linux="yes"
linux_user="yes"
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
kqemu="yes"
+ kvm="yes"
+fi
+if [ "$cpu" = "ia64" ] ; then
+ kvm="yes"
+ gdbstub="no"
+ slirp="no"
fi
;;
esac
@@ -278,8 +287,12 @@ for opt do
;;
--disable-kqemu) kqemu="no"
;;
+ --enable-kvm) kvm="yes"
+ ;;
--enable-profiler) profiler="yes"
;;
+ --kernel-path=*) kernel_path="$optarg"
+ ;;
--enable-cocoa) cocoa="yes" ; coreaudio="yes" ; sdl="no"
;;
--disable-gfx-check) check_gfx="no"
@@ -388,6 +401,8 @@ echo " --target-list=LIST set target list [$target_list]"
echo ""
echo "kqemu kernel acceleration support:"
echo " --disable-kqemu disable kqemu support"
+echo " --kernel-path=PATH set the kernel path (configure probes it)"
+echo " --enable-kvm enable kernel virtual machine support"
echo ""
echo "Advanced options (experts only):"
echo " --source-path=PATH path of source code [$source_path]"
@@ -740,6 +755,7 @@ if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu"
fi
echo "kqemu support $kqemu"
+echo "kvm support $kvm"
echo "Documentation $build_docs"
[ ! -z "$uname_release" ] && \
echo "uname -r $uname_release"
@@ -920,6 +936,7 @@ fi
qemu_version=`head $source_path/VERSION`
echo "VERSION=$qemu_version" >>$config_mak
echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
+echo "#define KVM_VERSION \"kvm-devel\"" >> $config_h
echo "SRC_PATH=$source_path" >> $config_mak
if [ "$source_path_used" = "yes" ]; then
@@ -1048,6 +1065,14 @@ elfload32="no"
interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
+configure_kvm() {
+ if test $kvm = "yes" -a "$target_softmmu" = "yes" -a \
+ \( "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "ia64" \); then
+ echo "#define USE_KVM 1" >> $config_h
+ echo "CONFIG_KVM_KERNEL_INC=$kernel_path/include" >> $config_mak
+ fi
+}
+
if test "$target_cpu" = "i386" ; then
echo "TARGET_ARCH=i386" >> $config_mak
echo "#define TARGET_ARCH \"i386\"" >> $config_h
@@ -1055,6 +1080,12 @@ if test "$target_cpu" = "i386" ; then
if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "i386" ; then
echo "#define USE_KQEMU 1" >> $config_h
fi
+ configure_kvm
+elif test "$target_cpu" = "ia64" ; then
+ echo "TARGET_ARCH=ia64" >> $config_mak
+ echo "#define TARGET_ARCH \"ia64\"" >> $config_h
+ echo "#define TARGET_IA64 1" >> $config_h
+ configure_kvm
elif test "$target_cpu" = "arm" -o "$target_cpu" = "armeb" ; then
echo "TARGET_ARCH=arm" >> $config_mak
echo "#define TARGET_ARCH \"arm\"" >> $config_h
@@ -1108,6 +1139,7 @@ elif test "$target_cpu" = "x86_64" ; then
if test $kqemu = "yes" -a "$target_softmmu" = "yes" -a $cpu = "x86_64" ; then
echo "#define USE_KQEMU 1" >> $config_h
fi
+ configure_kvm
elif test "$target_cpu" = "mips" -o "$target_cpu" = "mipsel" ; then
echo "TARGET_ARCH=mips" >> $config_mak
echo "#define TARGET_ARCH \"mips\"" >> $config_h
diff --git a/cpu-all.h b/cpu-all.h
index 7876a470e..a742b57cc 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -695,7 +695,7 @@ static inline void stfq_be_p(void *ptr, float64 v)
/* page related stuff */
-#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
+#define TARGET_PAGE_SIZE (1ul << TARGET_PAGE_BITS)
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
@@ -802,10 +802,11 @@ int cpu_inl(CPUState *env, int addr);
/* memory API */
-extern int phys_ram_size;
+extern ram_addr_t phys_ram_size;
extern int phys_ram_fd;
extern uint8_t *phys_ram_base;
extern uint8_t *phys_ram_dirty;
+extern uint8_t *bios_mem;
/* physical memory access */
#define TLB_INVALID_MASK (1 << 3)
@@ -829,7 +830,7 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr,
unsigned long size,
unsigned long phys_offset);
uint32_t cpu_get_physical_page_desc(target_phys_addr_t addr);
-ram_addr_t qemu_ram_alloc(unsigned int size);
+ram_addr_t qemu_ram_alloc(unsigned long size);
void qemu_ram_free(ram_addr_t addr);
int cpu_register_io_memory(int io_index,
CPUReadMemoryFunc **mem_read,
@@ -868,6 +869,7 @@ int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
#define VGA_DIRTY_FLAG 0x01
#define CODE_DIRTY_FLAG 0x02
+#define MIGRATION_DIRTY_FLAG 0x08
/* read dirty bit (return 0 or 1) */
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
@@ -890,6 +892,10 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
int dirty_flags);
void cpu_tlb_update_dirty(CPUState *env);
+int cpu_physical_memory_set_dirty_tracking(int enable);
+
+int cpu_physical_memory_get_dirty_tracking(void);
+
void dump_exec_info(FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
diff --git a/cpu-exec.c b/cpu-exec.c
index 8134c229c..3fe73f018 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -35,6 +35,11 @@
#include <sys/ucontext.h>
#endif
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+extern int kvm_allowed;
+#endif
+
int tb_invalidated_flag;
//#define DEBUG_EXEC
@@ -261,6 +266,10 @@ static inline TranslationBlock *tb_find_fast(void)
flags = 0;
cs_base = 0;
pc = env->pc;
+#elif defined(TARGET_IA64)
+ flags = 0;
+ cs_base = 0; /* XXXXX */
+ pc = 0;
#else
#error unsupported CPU
#endif
@@ -330,6 +339,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
@@ -385,6 +395,8 @@ int cpu_exec(CPUState *env1)
do_interrupt(env);
#elif defined(TARGET_M68K)
do_interrupt(0);
+#elif defined(TARGET_IA64)
+ do_interrupt(env);
#endif
}
env->exception_index = -1;
@@ -415,6 +427,12 @@ int cpu_exec(CPUState *env1)
}
#endif
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ kvm_cpu_exec(env);
+ longjmp(env->jmp_env, 1);
+ }
+#endif
T0 = 0; /* force lookup of first TB */
for(;;) {
SAVE_GLOBALS();
@@ -719,6 +737,7 @@ int cpu_exec(CPUState *env1)
| env->cc_dest | (env->cc_x << 4);
#elif defined(TARGET_MIPS)
#elif defined(TARGET_SH4)
+#elif defined(TARGET_IA64)
#elif defined(TARGET_ALPHA)
#elif defined(TARGET_CRIS)
/* XXXXX */
diff --git a/cutils.c b/cutils.c
index 9ef2fa627..608c31a5f 100644
--- a/cutils.c
+++ b/cutils.c
@@ -95,3 +95,43 @@ time_t mktimegm(struct tm *tm)
t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
return t;
}
+
+int hex2bin(char ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ else if (ch >= 'A' && ch <= 'Z')
+ return 10 + ch - 'A';
+ else if (ch >= 'a' && ch <= 'z')
+ return 10 + ch - 'a';
+
+ return -1;
+}
+
+char *urldecode(const char *ptr)
+{
+ char *ret;
+ int i;
+
+ ret = qemu_mallocz(strlen(ptr) + 1);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = 0; *ptr; ptr++, i++) {
+ switch (*ptr) {
+ case '%':
+ if (ptr[1] == 0 || ptr[2] == 0)
+ break;
+ ret[i] = hex2bin(ptr[1]) << 4 | hex2bin(ptr[2]);
+ ptr += 2;
+ break;
+ default:
+ ret[i] = *ptr;
+ break;
+ }
+ }
+ ret[i] = 0;
+
+ return ret;
+}
+
diff --git a/exec-all.h b/exec-all.h
index e7c480601..10281d329 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -441,11 +441,10 @@ static inline int testandset (int *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/exec.c b/exec.c
index 07d5de04a..d9e162554 100644
--- a/exec.c
+++ b/exec.c
@@ -34,6 +34,9 @@
#include "cpu.h"
#include "exec-all.h"
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+#endif
#if defined(CONFIG_USER_ONLY)
#include <qemu.h>
#endif
@@ -72,9 +75,18 @@
#define TARGET_VIRT_ADDR_SPACE_BITS 42
#elif defined(TARGET_PPC64)
#define TARGET_PHYS_ADDR_SPACE_BITS 42
-#else
+#elif USE_KQEMU
/* Note: for compatibility with kqemu, we use 32 bits for x86_64 */
#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#elif TARGET_X86_64
+#define TARGET_PHYS_ADDR_SPACE_BITS 42
+#else
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#endif
+
+#ifdef USE_KVM
+extern int kvm_allowed;
+extern kvm_context_t kvm_context;
#endif
TranslationBlock tbs[CODE_GEN_MAX_BLOCKS];
@@ -86,10 +98,12 @@ spinlock_t tb_lock = SPIN_LOCK_UNLOCKED;
uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE] __attribute__((aligned (32)));
uint8_t *code_gen_ptr;
-int phys_ram_size;
+ram_addr_t phys_ram_size;
int phys_ram_fd;
uint8_t *phys_ram_base;
uint8_t *phys_ram_dirty;
+uint8_t *bios_mem;
+static int in_migration;
static ram_addr_t phys_ram_alloc_offset = 0;
CPUState *first_cpu;
@@ -111,7 +125,7 @@ typedef struct PageDesc {
typedef struct PhysPageDesc {
/* offset in host memory of the page + io_index in the low 12 bits */
- uint32_t phys_offset;
+ ram_addr_t phys_offset;
} PhysPageDesc;
#define L2_BITS 10
@@ -1129,6 +1143,11 @@ int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
return -1;
env->breakpoints[env->nb_breakpoints++] = pc;
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_update_debugger(env);
+#endif
+
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1151,6 +1170,11 @@ int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
if (i < env->nb_breakpoints)
env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_update_debugger(env);
+#endif
+
breakpoint_invalidate(env, pc);
return 0;
#else
@@ -1169,6 +1193,10 @@ void cpu_single_step(CPUState *env, int enabled)
/* XXX: only flush what is necessary */
tb_flush(env);
}
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_update_debugger(env);
+#endif
#endif
}
@@ -1216,6 +1244,10 @@ void cpu_interrupt(CPUState *env, int mask)
static int interrupt_lock;
env->interrupt_request |= mask;
+#ifdef USE_KVM
+ if (kvm_allowed && !kvm_irqchip_in_kernel(kvm_context))
+ kvm_update_interrupt_request(env);
+#endif
/* if the cpu is currently executing code, we must unlink it and
all the potentially executing TB */
tb = env->current_tb;
@@ -1559,6 +1591,22 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
#endif
}
+int cpu_physical_memory_set_dirty_tracking(int enable)
+{
+ int r=0;
+
+#ifdef USE_KVM
+ r = kvm_physical_memory_set_dirty_tracking(enable);
+#endif
+ in_migration = enable;
+ return r;
+}
+
+int cpu_physical_memory_get_dirty_tracking(void)
+{
+ return in_migration;
+}
+
static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
{
ram_addr_t ram_addr;
@@ -2082,11 +2130,11 @@ uint32_t cpu_get_physical_page_desc(target_phys_addr_t addr)
}
/* XXX: better than nothing */
-ram_addr_t qemu_ram_alloc(unsigned int size)
+ram_addr_t qemu_ram_alloc(unsigned long size)
{
ram_addr_t addr;
- if ((phys_ram_alloc_offset + size) >= phys_ram_size) {
- fprintf(stderr, "Not enough memory (requested_size = %u, max memory = %d)\n",
+ if ((phys_ram_alloc_offset + size) > phys_ram_size) {
+ fprintf(stderr, "Not enough memory (requested_size = %lu, max memory = %d)\n",
size, phys_ram_size);
abort();
}
@@ -2602,6 +2650,9 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
(0xff & ~CODE_DIRTY_FLAG);
}
+#ifdef __ia64__
+ kvm_sync_icache((unsigned long)ptr, l);
+#endif
}
} else {
if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
@@ -2759,6 +2810,14 @@ uint32_t lduw_phys(target_phys_addr_t addr)
return tswap16(val);
}
+#ifdef __GNUC__
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define likely(x) x
+#define unlikely(x) x
+#endif
+
/* warning: addr must be aligned. The ram page is not masked as dirty
and the code inside is not invalidated. It is useful if the dirty
bits are used to track modified PTEs */
@@ -2780,9 +2839,19 @@ void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val)
io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
} else {
- ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
- (addr & ~TARGET_PAGE_MASK);
+ unsigned long addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ ptr = phys_ram_base + addr1;
stl_p(ptr, val);
+
+ if (unlikely(in_migration)) {
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
+ /* set dirty bit */
+ phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
+ (0xff & ~CODE_DIRTY_FLAG);
+ }
+ }
}
}
diff --git a/gdbstub.c b/gdbstub.c
index 64f4a9fff..66a4d5681 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -33,6 +33,7 @@
#include "qemu-char.h"
#include "sysemu.h"
#include "gdbstub.h"
+#include "qemu-kvm.h"
#endif
#include "qemu_socket.h"
@@ -224,7 +225,141 @@ static int put_packet(GDBState *s, char *buf)
return 0;
}
-#if defined(TARGET_I386)
+#if defined(TARGET_X86_64)
+
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+ uint8_t *p = mem_buf;
+ int i, fpus;
+
+#define PUTREG(x) do { \
+ target_ulong reg = tswapl(x); \
+ memcpy(p, &reg, sizeof reg); \
+ p += sizeof reg; \
+ } while (0)
+#define PUTREG32(x) do { \
+ uint32_t reg = tswap32(x); \
+ memcpy(p, &reg, sizeof reg); \
+ p += sizeof reg; \
+ } while (0)
+#define PUTREGF(x) do { \
+ memcpy(p, &(x), 10); \
+ p += sizeof (x); \
+ } while (0)
+
+ PUTREG(env->regs[R_EAX]);
+ PUTREG(env->regs[R_EBX]);
+ PUTREG(env->regs[R_ECX]);
+ PUTREG(env->regs[R_EDX]);
+ PUTREG(env->regs[R_ESI]);
+ PUTREG(env->regs[R_EDI]);
+ PUTREG(env->regs[R_EBP]);
+ PUTREG(env->regs[R_ESP]);
+ PUTREG(env->regs[8]);
+ PUTREG(env->regs[9]);
+ PUTREG(env->regs[10]);
+ PUTREG(env->regs[11]);
+ PUTREG(env->regs[12]);
+ PUTREG(env->regs[13]);
+ PUTREG(env->regs[14]);
+ PUTREG(env->regs[15]);
+
+ PUTREG(env->eip);
+ PUTREG32(env->eflags);
+ PUTREG32(env->segs[R_CS].selector);
+ PUTREG32(env->segs[R_SS].selector);
+ PUTREG32(env->segs[R_DS].selector);
+ PUTREG32(env->segs[R_ES].selector);
+ PUTREG32(env->segs[R_FS].selector);
+ PUTREG32(env->segs[R_GS].selector);
+ /* XXX: convert floats */
+ for(i = 0; i < 8; i++) {
+ PUTREGF(env->fpregs[i]);
+ }
+ PUTREG32(env->fpuc);
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ PUTREG32(fpus);
+ PUTREG32(0); /* XXX: convert tags */
+ PUTREG32(0); /* fiseg */
+ PUTREG32(0); /* fioff */
+ PUTREG32(0); /* foseg */
+ PUTREG32(0); /* fooff */
+ PUTREG32(0); /* fop */
+
+#undef PUTREG
+#undef PUTREG32
+#undef PUTREGF
+
+ return p - mem_buf;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+ uint8_t *p = mem_buf;
+ uint32_t junk;
+ int i, fpus;
+
+#define GETREG(x) do { \
+ target_ulong reg; \
+ memcpy(&reg, p, sizeof reg); \
+ x = tswapl(reg); \
+ p += sizeof reg; \
+ } while (0)
+#define GETREG32(x) do { \
+ uint32_t reg; \
+ memcpy(&reg, p, sizeof reg); \
+ x = tswap32(reg); \
+ p += sizeof reg; \
+ } while (0)
+#define GETREGF(x) do { \
+ memcpy(&(x), p, 10); \
+ p += 10; \
+ } while (0)
+
+ GETREG(env->regs[R_EAX]);
+ GETREG(env->regs[R_EBX]);
+ GETREG(env->regs[R_ECX]);
+ GETREG(env->regs[R_EDX]);
+ GETREG(env->regs[R_ESI]);
+ GETREG(env->regs[R_EDI]);
+ GETREG(env->regs[R_EBP]);
+ GETREG(env->regs[R_ESP]);
+ GETREG(env->regs[8]);
+ GETREG(env->regs[9]);
+ GETREG(env->regs[10]);
+ GETREG(env->regs[11]);
+ GETREG(env->regs[12]);
+ GETREG(env->regs[13]);
+ GETREG(env->regs[14]);
+ GETREG(env->regs[15]);
+
+ GETREG(env->eip);
+ GETREG32(env->eflags);
+ GETREG32(env->segs[R_CS].selector);
+ GETREG32(env->segs[R_SS].selector);
+ GETREG32(env->segs[R_DS].selector);
+ GETREG32(env->segs[R_ES].selector);
+ GETREG32(env->segs[R_FS].selector);
+ GETREG32(env->segs[R_GS].selector);
+ /* XXX: convert floats */
+ for(i = 0; i < 8; i++) {
+ GETREGF(env->fpregs[i]);
+ }
+ GETREG32(env->fpuc);
+ GETREG32(fpus); /* XXX: convert fpus */
+ GETREG32(junk); /* XXX: convert tags */
+ GETREG32(junk); /* fiseg */
+ GETREG32(junk); /* fioff */
+ GETREG32(junk); /* foseg */
+ GETREG32(junk); /* fooff */
+ GETREG32(junk); /* fop */
+
+#undef GETREG
+#undef GETREG32
+#undef GETREGF
+}
+
+#elif defined(TARGET_I386)
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
{
@@ -893,6 +1028,9 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
env->eip = addr;
+#ifdef USE_KVM
+ kvm_load_registers(env);
+#endif
#elif defined (TARGET_PPC)
env->nip = addr;
#elif defined (TARGET_SPARC)
@@ -919,6 +1057,9 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
env->eip = addr;
+#ifdef USE_KVM
+ kvm_load_registers(env);
+#endif
#elif defined (TARGET_PPC)
env->nip = addr;
#elif defined (TARGET_SPARC)
@@ -970,6 +1111,9 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
}
break;
case 'g':
+#ifdef USE_KVM
+ kvm_save_registers(env);
+#endif
reg_size = cpu_gdb_read_registers(env, mem_buf);
memtohex(buf, mem_buf, reg_size);
put_packet(s, buf);
@@ -979,6 +1123,9 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
len = strlen(p) / 2;
hextomem((uint8_t *)registers, p, len);
cpu_gdb_write_registers(env, mem_buf, len);
+#ifdef USE_KVM
+ kvm_load_registers(env);
+#endif
put_packet(s, "OK");
break;
case 'm':
diff --git a/hw/apic.c b/hw/apic.c
index db02f9a98..a47c36637 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -21,6 +21,12 @@
#include "pc.h"
#include "qemu-timer.h"
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+extern int kvm_allowed;
+extern kvm_context_t kvm_context;
+#endif
+
//#define DEBUG_APIC
//#define DEBUG_IOAPIC
@@ -56,6 +62,7 @@
#define APIC_INPUT_POLARITY (1<<13)
#define APIC_SEND_PENDING (1<<12)
+/* FIXME: it's now hard coded to be equal with KVM_IOAPIC_NUM_PINS */
#define IOAPIC_NUM_PINS 0x18
#define ESR_ILLEGAL_ADDRESS (1 << 7)
@@ -400,6 +407,11 @@ static void apic_init_ipi(APICState *s)
s->initial_count = 0;
s->initial_count_load_time = 0;
s->next_time = 0;
+#ifdef USE_KVM
+ if (kvm_allowed && !kvm_irqchip_in_kernel(kvm_context))
+ if (s->cpu_env)
+ kvm_apic_init(s->cpu_env);
+#endif
}
/* send a SIPI message to the CPU to start it */
@@ -412,6 +424,10 @@ static void apic_startup(APICState *s, int vector_num)
cpu_x86_load_seg_cache(env, R_CS, vector_num << 8, vector_num << 12,
0xffff, 0);
env->hflags &= ~HF_HALTED_MASK;
+#if USE_KVM
+ if (kvm_allowed && !kvm_irqchip_in_kernel(kvm_context))
+ kvm_update_after_sipi(env);
+#endif
}
static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode,
@@ -737,11 +753,100 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
}
+#ifdef USE_KVM
+
+#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(kvm_context, s->cpu_env->cpu_index, kapic);
+
+ s->id = kapic_reg(kapic, 0x2);
+ 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);
+ 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(kvm_context, s->cpu_env->cpu_index, klapic);
+}
+
+#endif
+
+#endif
+
static void apic_save(QEMUFile *f, void *opaque)
{
APICState *s = opaque;
int i;
+#ifdef USE_KVM
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_lapic_save_to_user(s);
+ }
+#endif
+#endif
+
qemu_put_be32s(f, &s->apicbase);
qemu_put_8s(f, &s->id);
qemu_put_8s(f, &s->arb_id);
@@ -804,6 +909,15 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id)
if (version_id >= 2)
qemu_get_timer(f, s->timer);
+
+#ifdef USE_KVM
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_lapic_load_from_user(s);
+ }
+#endif
+#endif
+
return 0;
}
@@ -818,6 +932,13 @@ static void apic_reset(void *opaque)
* processor when local APIC is enabled.
*/
s->lvt[APIC_LVT_LINT0] = 0x700;
+#ifdef USE_KVM
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_lapic_load_from_user(s);
+ }
+#endif
+#endif
}
static CPUReadMemoryFunc *apic_mem_read[3] = {
@@ -1010,11 +1131,58 @@ static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t va
}
}
+#ifdef USE_KVM
+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;
+ 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;
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ kioapic->redirtbl[i].bits = s->ioredtbl[i];
+ }
+
+ kvm_set_irqchip(kvm_context, &chip);
+#endif
+}
+#endif
+
static void ioapic_save(QEMUFile *f, void *opaque)
{
IOAPICState *s = opaque;
int i;
+#ifdef USE_KVM
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_ioapic_save_to_user(s);
+ }
+#endif
+
qemu_put_8s(f, &s->id);
qemu_put_8s(f, &s->ioregsel);
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
@@ -1035,6 +1203,13 @@ static int ioapic_load(QEMUFile *f, void *opaque, int version_id)
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
qemu_get_be64s(f, &s->ioredtbl[i]);
}
+
+#ifdef USE_KVM
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_ioapic_load_from_user(s);
+ }
+#endif
+
return 0;
}
diff --git a/hw/boards.h b/hw/boards.h
index 712d3cd39..079760d02 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -3,7 +3,7 @@
#ifndef HW_BOARDS_H
#define HW_BOARDS_H
-typedef void QEMUMachineInitFunc(int ram_size, int vga_ram_size,
+typedef void QEMUMachineInitFunc(ram_addr_t ram_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
const char *kernel_filename,
const char *kernel_cmdline,
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 59bfdff56..ee7ec1c1e 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -31,6 +31,9 @@
#include "pci.h"
#include "console.h"
#include "vga_int.h"
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
/*
* TODO:
@@ -234,6 +237,13 @@ typedef struct CirrusVGAState {
int cirrus_linear_io_addr;
int cirrus_linear_bitblt_io_addr;
int cirrus_mmio_io_addr;
+#ifdef USE_KVM
+ unsigned long cirrus_lfb_addr;
+ unsigned long cirrus_lfb_end;
+ int aliases_enabled;
+ uint32_t aliased_bank_base[2];
+ uint32_t aliased_bank_limit[2];
+#endif
uint32_t cirrus_addr_mask;
uint32_t linear_mmio_mask;
uint8_t cirrus_shadow_gr0;
@@ -1354,6 +1364,8 @@ cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value)
printf("cirrus: handled outport sr_index %02x, sr_value %02x\n",
reg_index, reg_value);
#endif
+ if (reg_index == 0x07)
+ cirrus_update_memory_access(s);
break;
case 0x17: // Configuration Readback and Extended Control
s->sr[reg_index] = (s->sr[reg_index] & 0x38) | (reg_value & 0xc7);
@@ -1500,6 +1512,7 @@ cirrus_hook_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
s->gr[reg_index] = reg_value;
cirrus_update_bank_ptr(s, 0);
cirrus_update_bank_ptr(s, 1);
+ cirrus_update_memory_access(s);
break;
case 0x0B:
s->gr[reg_index] = reg_value;
@@ -2588,10 +2601,90 @@ static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = {
cirrus_linear_bitblt_writel,
};
+#ifdef USE_KVM
+
+#include "qemu-kvm.h"
+
+extern kvm_context_t kvm_context;
+
+void *set_vram_mapping(unsigned long begin, unsigned long end)
+{
+ void *vram_pointer = NULL;
+
+ /* align begin and end address */
+ begin = begin & TARGET_PAGE_MASK;
+ end = begin + VGA_RAM_SIZE;
+ end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK;
+
+ vram_pointer = kvm_create_phys_mem(kvm_context, begin, end - begin,
+ 1, 1);
+
+ if (vram_pointer == NULL) {
+ printf("set_vram_mapping: cannot allocate memory: %m\n");
+ return NULL;
+ }
+
+ memset(vram_pointer, 0, end - begin);
+
+ return vram_pointer;
+}
+
+int unset_vram_mapping(unsigned long begin, unsigned long end)
+{
+ /* align begin and end address */
+ end = begin + VGA_RAM_SIZE;
+ begin = begin & TARGET_PAGE_MASK;
+ end = (end + TARGET_PAGE_SIZE -1 ) & TARGET_PAGE_MASK;
+
+ kvm_destroy_phys_mem(kvm_context, begin, end - begin);
+
+ return 0;
+}
+#ifdef CONFIG_X86
+static void kvm_update_vga_alias(CirrusVGAState *s, int ok, int bank,
+ unsigned long phys_addr)
+{
+ unsigned limit, base;
+
+ if (!ok && !s->aliases_enabled)
+ return;
+ limit = s->cirrus_bank_limit[bank];
+ if (limit > 0x8000)
+ limit = 0x8000;
+ base = s->cirrus_lfb_addr + s->cirrus_bank_base[bank];
+ if (ok) {
+ if (!s->aliases_enabled
+ || base != s->aliased_bank_base[bank]
+ || limit != s->aliased_bank_limit[bank]) {
+ kvm_create_memory_alias(kvm_context, phys_addr,
+ 0xa0000 + bank * 0x8000,
+ limit, base);
+ s->aliased_bank_base[bank] = base;
+ s->aliased_bank_limit[bank] = limit;
+ }
+ } else {
+ kvm_destroy_memory_alias(kvm_context, phys_addr);
+ }
+}
+
+static void kvm_update_vga_aliases(CirrusVGAState *s, int ok)
+{
+ if (kvm_allowed) {
+ kvm_update_vga_alias(s, ok, 0, 0xc0000);
+ kvm_update_vga_alias(s, ok, 1, s->map_addr);
+ }
+ s->aliases_enabled = ok;
+}
+#endif
+#endif
+
/* Compute the memory access functions */
static void cirrus_update_memory_access(CirrusVGAState *s)
{
unsigned mode;
+#if defined(USE_KVM) && defined(CONFIG_X86)
+ int want_vga_alias = 0;
+#endif
if ((s->sr[0x17] & 0x44) == 0x44) {
goto generic_io;
@@ -2606,16 +2699,62 @@ static void cirrus_update_memory_access(CirrusVGAState *s)
mode = s->gr[0x05] & 0x7;
if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) {
+#ifdef USE_KVM
+ if (kvm_allowed && s->cirrus_lfb_addr && s->cirrus_lfb_end &&
+ !s->map_addr) {
+ void *vram_pointer, *old_vram;
+
+ vram_pointer = set_vram_mapping(s->cirrus_lfb_addr,
+ s->cirrus_lfb_end);
+ if (!vram_pointer)
+ fprintf(stderr, "NULL vram_pointer\n");
+ else {
+ old_vram = vga_update_vram((VGAState *)s, vram_pointer,
+ VGA_RAM_SIZE);
+ qemu_free(old_vram);
+ }
+ s->map_addr = s->cirrus_lfb_addr;
+ s->map_end = s->cirrus_lfb_end;
+ }
+#ifdef CONFIG_X86
+ if (kvm_allowed
+ && !(s->cirrus_srcptr != s->cirrus_srcptr_end)
+ && !((s->sr[0x07] & 0x01) == 0)
+ && !((s->gr[0x0B] & 0x14) == 0x14)
+ && !(s->gr[0x0B] & 0x02))
+ want_vga_alias = 1;
+#endif
+#endif
s->cirrus_linear_write[0] = cirrus_linear_mem_writeb;
s->cirrus_linear_write[1] = cirrus_linear_mem_writew;
s->cirrus_linear_write[2] = cirrus_linear_mem_writel;
} else {
generic_io:
+#ifdef USE_KVM
+ if (kvm_allowed && s->cirrus_lfb_addr && s->cirrus_lfb_end &&
+ s->map_addr) {
+ int error;
+ void *old_vram = NULL;
+
+ error = unset_vram_mapping(s->cirrus_lfb_addr,
+ s->cirrus_lfb_end);
+ if (!error)
+ old_vram = vga_update_vram((VGAState *)s, NULL,
+ VGA_RAM_SIZE);
+ if (old_vram)
+ munmap(old_vram, s->map_end - s->map_addr);
+ s->map_addr = s->map_end = 0;
+ }
+#endif
s->cirrus_linear_write[0] = cirrus_linear_writeb;
s->cirrus_linear_write[1] = cirrus_linear_writew;
s->cirrus_linear_write[2] = cirrus_linear_writel;
}
}
+#if defined(USE_KVM) && defined(CONFIG_X86)
+ kvm_update_vga_aliases(s, want_vga_alias);
+#endif
+
}
@@ -3009,6 +3148,13 @@ static void cirrus_vga_save(QEMUFile *f, void *opaque)
qemu_put_be32s(f, &s->hw_cursor_y);
/* XXX: we do not save the bitblt state - we assume we do not save
the state when the blitter is active */
+
+#ifdef USE_KVM
+ if (kvm_allowed) { /* XXX: KVM images ought to be loadable in QEMU */
+ qemu_put_be32s(f, &s->real_vram_size);
+ qemu_put_buffer(f, s->vram_ptr, s->real_vram_size);
+ }
+#endif
}
static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id)
@@ -3059,6 +3205,22 @@ static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_be32s(f, &s->hw_cursor_x);
qemu_get_be32s(f, &s->hw_cursor_y);
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ int real_vram_size;
+ qemu_get_be32s(f, &real_vram_size);
+ if (real_vram_size != s->real_vram_size) {
+ if (real_vram_size > s->real_vram_size)
+ real_vram_size = s->real_vram_size;
+ printf("%s: REAL_VRAM_SIZE MISMATCH !!!!!! SAVED=%d CURRENT=%d",
+ __FUNCTION__, real_vram_size, s->real_vram_size);
+ }
+ qemu_get_buffer(f, s->vram_ptr, real_vram_size);
+ cirrus_update_memory_access(s);
+ }
+#endif
+
+
/* force refresh */
s->graphic_mode = -1;
cirrus_update_bank_ptr(s, 0);
@@ -3214,6 +3376,17 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int region_num,
/* XXX: add byte swapping apertures */
cpu_register_physical_memory(addr, s->vram_size,
s->cirrus_linear_io_addr);
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ s->cirrus_lfb_addr = addr;
+ s->cirrus_lfb_end = addr + VGA_RAM_SIZE;
+
+ if (s->map_addr && (s->cirrus_lfb_addr != s->map_addr) &&
+ (s->cirrus_lfb_end != s->map_end))
+ printf("cirrus vga map change while on lfb mode\n");
+ }
+#endif
+
cpu_register_physical_memory(addr + 0x1000000, 0x400000,
s->cirrus_linear_bitblt_io_addr);
}
diff --git a/hw/hw.h b/hw/hw.h
index 3589adee3..6ce233948 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -7,7 +7,14 @@
/* VM Load/Save */
-QEMUFile *qemu_fopen(const char *filename, const char *mode);
+typedef void (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, int64_t pos, int size);
+typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, int64_t pos, int size);
+typedef void (QEMUFileCloseFunc)(void *opaque);
+
+QEMUFile *qemu_fopen(void *opaque, QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer, QEMUFileCloseFunc *close);
+QEMUFile *qemu_fopen_file(const char *filename, const char *mode);
+QEMUFile *qemu_fopen_fd(int fd);
void qemu_fflush(QEMUFile *f);
void qemu_fclose(QEMUFile *f);
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
diff --git a/hw/hypercall.c b/hw/hypercall.c
new file mode 100644
index 000000000..73f6bb159
--- /dev/null
+++ b/hw/hypercall.c
@@ -0,0 +1,362 @@
+/*
+ * QEMU-KVM Hypercall emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2006 Qumranet
+ *
+ * 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/hw.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "hw/isa.h"
+#include "hw/irq.h"
+#include "hw/pci.h"
+#include "hypercall.h"
+#include <stddef.h>
+
+#define HYPERCALL_IOPORT_SIZE 0x100
+
+static int use_hypercall_dev = 0;
+
+typedef struct VmChannelCharDriverState {
+ CharDriverState *vmchannel_hd;
+ uint32_t deviceid;
+} VmChannelCharDriverState;
+
+static VmChannelCharDriverState vmchannel_hds[MAX_VMCHANNEL_DEVICES];
+
+typedef struct HypercallState {
+ uint32_t hcr;
+ uint32_t hsr;
+ uint32_t txsize;
+ uint32_t txbuff;
+ uint32_t rxsize;
+ uint8_t RxBuff[HP_MEM_SIZE];
+ uint8_t txbufferaccu[HP_MEM_SIZE];
+ int txbufferaccu_offset;
+ int irq;
+ PCIDevice *pci_dev;
+ uint32_t index;
+} HypercallState;
+
+static HypercallState *pHypercallStates[MAX_VMCHANNEL_DEVICES] = {NULL};
+
+//#define HYPERCALL_DEBUG 1
+
+static void hp_reset(HypercallState *s)
+{
+ s->hcr = HCR_DI;
+ s->hsr = 0;
+ s->txsize = 0;
+ s->txbuff = 0;
+ s->rxsize= 0;
+ s->txbufferaccu_offset = 0;
+}
+
+static void hypercall_update_irq(HypercallState *s);
+
+
+static void hp_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ HypercallState *s = opaque;
+
+#ifdef HYPERCALL_DEBUG
+ printf("%s: addr=0x%x, val=0x%x\n", __FUNCTION__, addr, val);
+#endif
+ addr &= 0xff;
+
+ switch(addr)
+ {
+ case HCR_REGISTER:
+ {
+ s->hcr = val;
+ if (s->hcr & HCR_DI)
+ hypercall_update_irq(s);
+ if (val & HCR_GRS){
+ hp_reset(s);
+ }
+ break;
+ }
+
+ case HP_TXSIZE:
+ {
+ // handle the case when the we are being called when txsize is not 0
+ if (s->txsize != 0) {
+ printf("txsize is being set, but txsize is not 0!!!\n");
+ }
+ if (val > HP_MEM_SIZE) {
+ printf("txsize is larger than allowed by hw!!!\n");
+ }
+ s->txsize = val;
+ s->txbufferaccu_offset = 0;
+ break;
+ }
+
+ case HP_TXBUFF:
+ {
+ if (s->txsize == 0) {
+ printf("error with txbuff!!!\n");
+ break;
+ }
+
+ s->txbufferaccu[s->txbufferaccu_offset] = val;
+ s->txbufferaccu_offset++;
+ if (s->txbufferaccu_offset >= s->txsize) {
+ qemu_chr_write(vmchannel_hds[s->index].vmchannel_hd, s->txbufferaccu, s->txsize);
+ s->txbufferaccu_offset = 0;
+ s->txsize = 0;
+ }
+ break;
+ }
+ default:
+ {
+ printf("hp_ioport_write to unhandled address!!!\n");
+ }
+ }
+}
+
+static uint32_t hp_ioport_read(void *opaque, uint32_t addr)
+{
+ HypercallState *s = opaque;
+ int ret;
+
+ addr &= 0xff;
+#ifdef HYPERCALL_DEBUG
+ // Since HSR_REGISTER is being repeatedly read in the guest ISR we don't print it
+ if (addr != HSR_REGISTER)
+ printf("%s: addr=0x%x", __FUNCTION__, addr);
+#endif
+
+ if (addr >= offsetof(HypercallState, RxBuff) )
+ {
+ int RxBuffOffset = addr - (offsetof(HypercallState, RxBuff));
+ ret = s->RxBuff[RxBuffOffset];
+#ifdef HYPERCALL_DEBUG
+ printf(" val=%x\n", ret);
+#endif
+ return ret;
+ }
+
+ switch (addr)
+ {
+ case HSR_REGISTER:
+ ret = s->hsr;
+ if (ret & HSR_VDR) {
+ s->hsr &= ~HSR_VDR;
+ }
+ break;
+ case HP_RXSIZE:
+ ret = s->rxsize;
+ break;
+
+ default:
+ ret = 0x00;
+ break;
+ }
+#ifdef HYPERCALL_DEBUG
+ printf(" val=%x\n", ret);
+#endif
+ return ret;
+}
+
+/***********************************************************/
+/* PCI Hypercall definitions */
+
+typedef struct PCIHypercallState {
+ PCIDevice dev;
+ HypercallState hp;
+} PCIHypercallState;
+
+static void hp_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIHypercallState *d = (PCIHypercallState *)pci_dev;
+ HypercallState *s = &d->hp;
+
+ register_ioport_write(addr, HYPERCALL_IOPORT_SIZE, 1, hp_ioport_write, s);
+ register_ioport_read(addr, HYPERCALL_IOPORT_SIZE, 1, hp_ioport_read, s);
+
+}
+
+
+static void hypercall_update_irq(HypercallState *s)
+{
+ /* PCI irq */
+ qemu_set_irq(s->pci_dev->irq[0], !(s->hcr & HCR_DI));
+}
+
+static void hc_save(QEMUFile* f,void* opaque)
+{
+ HypercallState* s=(HypercallState*)opaque;
+
+ pci_device_save(s->pci_dev, f);
+
+ qemu_put_be32s(f, &s->hcr);
+ qemu_put_be32s(f, &s->hsr);
+ qemu_put_be32s(f, &s->txsize);
+ qemu_put_be32s(f, &s->txbuff);
+ qemu_put_be32s(f, &s->rxsize);
+ qemu_put_buffer(f, s->RxBuff, HP_MEM_SIZE);
+ qemu_put_buffer(f, s->txbufferaccu, HP_MEM_SIZE);
+ qemu_put_be32s(f, &s->txbufferaccu_offset);
+ qemu_put_be32s(f, &s->irq);
+ qemu_put_be32s(f, &s->index);
+
+}
+
+static int hc_load(QEMUFile* f,void* opaque,int version_id)
+{
+ HypercallState* s=(HypercallState*)opaque;
+ int ret;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ ret = pci_device_load(s->pci_dev, f);
+ if (ret < 0)
+ return ret;
+
+ qemu_get_be32s(f, &s->hcr);
+ qemu_get_be32s(f, &s->hsr);
+ qemu_get_be32s(f, &s->txsize);
+ qemu_get_be32s(f, &s->txbuff);
+ qemu_get_be32s(f, &s->rxsize);
+ qemu_get_buffer(f, s->RxBuff, HP_MEM_SIZE);
+ qemu_get_buffer(f, s->txbufferaccu, HP_MEM_SIZE);
+ qemu_get_be32s(f, &s->txbufferaccu_offset);
+ qemu_get_be32s(f, &s->irq);
+ qemu_get_be32s(f, &s->index);
+
+ return 0;
+}
+
+static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t index)
+{
+ PCIHypercallState *d;
+ HypercallState *s;
+ uint8_t *pci_conf;
+ char name[sizeof("HypercallX")];
+
+ sprintf(name, "Hypercall%d", index);
+
+#ifdef HYPERCALL_DEBUG
+ printf("%s, devicename:%s\n", __FUNCTION__, name);
+#endif
+
+ // If the vmchannel wasn't initialized, we don't want the Hypercall device in the guest
+ if (use_hypercall_dev == 0) {
+ return;
+ }
+
+ d = (PCIHypercallState *)pci_register_device(bus,
+ name, sizeof(PCIHypercallState),
+ -1,
+ NULL, NULL);
+
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0x02; // Qumranet vendor ID 0x5002
+ pci_conf[0x01] = 0x50;
+ pci_conf[0x02] = deviceid & 0x00ff;
+ pci_conf[0x03] = (deviceid & 0xff00) >> 8;
+
+ pci_conf[0x09] = 0x00; // ProgIf
+ pci_conf[0x0a] = 0x00; // SubClass
+ pci_conf[0x0b] = 0x05; // BaseClass
+
+ pci_conf[0x0e] = 0x00; // header_type
+ pci_conf[0x3d] = 1; // interrupt pin 0
+
+ pci_register_io_region(&d->dev, 0, HYPERCALL_IOPORT_SIZE,
+ PCI_ADDRESS_SPACE_IO, hp_map);
+ s = &d->hp;
+ pHypercallStates[index] = s;
+ s->index = index;
+ s->irq = 16; /* PCI interrupt */
+ s->pci_dev = (PCIDevice *)d;
+
+ hp_reset(s);
+ register_savevm(name, index, 1, hc_save, hc_load, s);
+}
+
+void pci_hypercall_init(PCIBus *bus)
+{
+ int i;
+
+ // loop devices & call pci_hypercall_single_init with device id's
+ for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++){
+ if (vmchannel_hds[i].vmchannel_hd) {
+ pci_hypercall_single_init(bus, vmchannel_hds[i].deviceid, i);
+ }
+ }
+}
+
+static int vmchannel_can_read(void *opaque)
+{
+ return 128;
+}
+
+static void vmchannel_event(void *opaque, int event)
+{
+
+#ifdef HYPERCALL_DEBUG
+ // if index is to be used outside the printf, take it out of the #ifdef block!
+ long index = (long)opaque;
+ printf("%s index:%ld, got event %i\n", __FUNCTION__, index, event);
+#endif
+
+ return;
+}
+
+// input from vmchannel outside caller
+static void vmchannel_read(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+ long index = (long)opaque;
+
+#ifdef HYPERCALL_DEBUG
+ printf("vmchannel_read buf size:%d\n", size);
+#endif
+
+ // if the hypercall device is in interrupts disabled state, don't accept the data
+ if (pHypercallStates[index]->hcr & HCR_DI) {
+ return;
+ }
+
+ for(i = 0; i < size; i++) {
+ pHypercallStates[index]->RxBuff[i] = buf[i];
+ }
+ pHypercallStates[index]->rxsize = size;
+ pHypercallStates[index]->hsr = HSR_VDR;
+ hypercall_update_irq(pHypercallStates[index]);
+}
+
+void vmchannel_init(CharDriverState *hd, uint32_t deviceid, uint32_t index)
+{
+#ifdef HYPERCALL_DEBUG
+ printf("vmchannel_init, index=%d, deviceid=0x%x\n", index, deviceid);
+#endif
+
+ vmchannel_hds[index].deviceid = deviceid;
+ vmchannel_hds[index].vmchannel_hd = hd;
+
+ use_hypercall_dev = 1;
+ qemu_chr_add_handlers(vmchannel_hds[index].vmchannel_hd, vmchannel_can_read, vmchannel_read,
+ vmchannel_event, (void *)(long)index);
+}
diff --git a/hw/hypercall.h b/hw/hypercall.h
new file mode 100644
index 000000000..97434a785
--- /dev/null
+++ b/hw/hypercall.h
@@ -0,0 +1,45 @@
+/*
+ * QEMU-KVM Hypercall emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2006 Qumranet
+ *
+ * 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.
+ */
+
+#define HCR_REGISTER 0x00 // Hypercall Command Register WR
+#define HSR_REGISTER 0x04 // Hypercall Status Register RD
+#define HP_TXSIZE 0x08
+#define HP_TXBUFF 0x0c
+#define HP_RXSIZE 0x10
+#define HP_RXBUFF 0x14
+
+// HCR_REGISTER commands
+#define HCR_DI 1 // disable interrupts
+#define HCR_EI 2 // enable interrupts
+#define HCR_GRS 4 // Global reset
+#define HCR_RESET (HCR_GRS|HCR_DI)
+
+
+// Bits in HSR_REGISTER
+#define HSR_VDR 0x01 // vmchannel Data is ready to be read
+
+#define HP_MEM_SIZE 0xE0
+
+
diff --git a/hw/i8254.c b/hw/i8254.c
index 4813b0374..c2816802b 100644
--- a/hw/i8254.c
+++ b/hw/i8254.c
@@ -360,6 +360,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,6 +375,24 @@ 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,
diff --git a/hw/i8259.c b/hw/i8259.c
index add63450f..071f9f193 100644
--- a/hw/i8259.c
+++ b/hw/i8259.c
@@ -26,6 +26,10 @@
#include "isa.h"
#include "console.h"
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+#endif
+
/* debug PIC */
//#define DEBUG_PIC
@@ -181,7 +185,15 @@ int64_t irq_time[16];
static void i8259_set_irq(void *opaque, int irq, int level)
{
PicState2 *s = opaque;
+#ifdef USE_KVM
+#ifdef KVM_CAP_IRQCHIP
+ extern int kvm_set_irq(int irq, int level);
+ if (kvm_allowed)
+ if (kvm_set_irq(irq, level))
+ return;
+#endif
+#endif
#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
if (level != irq_level[irq]) {
#if defined(DEBUG_PIC)
@@ -215,18 +227,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) {
@@ -448,10 +477,85 @@ static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
return s->elcr;
}
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+extern int kvm_allowed;
+extern kvm_context_t kvm_context;
+
+static void kvm_kernel_pic_save_to_user(PicState *s)
+{
+#if defined(KVM_CAP_IRQCHIP) && 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(KVM_CAP_IRQCHIP) && 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
+}
+#endif
+
static void pic_save(QEMUFile *f, void *opaque)
{
PicState *s = opaque;
+#ifdef USE_KVM
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_pic_save_to_user(s);
+ }
+#endif
+
qemu_put_8s(f, &s->last_irr);
qemu_put_8s(f, &s->irr);
qemu_put_8s(f, &s->imr);
@@ -493,6 +597,13 @@ 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);
+
+#ifdef USE_KVM
+ if (kvm_allowed && kvm_irqchip_in_kernel(kvm_context)) {
+ kvm_kernel_pic_load_from_user(s);
+ }
+#endif
+
return 0;
}
diff --git a/hw/ipf.c b/hw/ipf.c
new file mode 100644
index 000000000..03df73d30
--- /dev/null
+++ b/hw/ipf.c
@@ -0,0 +1,569 @@
+/*
+ * Itanium Platforma 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 "vl.h"
+#include "firmware.h"
+#include "ia64intrin.h"
+#include <unistd.h>
+
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+extern int kvm_allowed;
+#endif
+
+/* output Bochs bios info messages */
+//#define DEBUG_BIOS
+
+#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
+
+static fdctrl_t *floppy_controller;
+static RTCState *rtc_state;
+static PCIDevice *i440fx_state;
+
+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 };
+
+/* 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, &sectors);
+ 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);
+}
+
+/* hd_table must contain 4 block drivers */
+static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, int boot_device, BlockDriverState **hd_table, int smp_cpus)
+{
+ RTCState *s = rtc_state;
+ 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);
+
+ switch(boot_device) {
+ case 'a':
+ case 'b':
+ rtc_set_memory(s, 0x3d, 0x01); /* floppy boot */
+ if (!fd_bootchk)
+ rtc_set_memory(s, 0x38, 0x01); /* disable signature check */
+ break;
+ default:
+ case 'c':
+ rtc_set_memory(s, 0x3d, 0x02); /* hard drive boot */
+ break;
+ case 'd':
+ rtc_set_memory(s, 0x3d, 0x03); /* CD-ROM boot */
+ break;
+ case 'n':
+ rtc_set_memory(s, 0x3d, 0x04); /* Network boot */
+ break;
+ }
+
+ /* 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, &sectors);
+ 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);
+}
+
+
+#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++;
+}
+
+#ifdef USE_KVM
+extern kvm_context_t kvm_context;
+extern int kvm_allowed;
+
+void kvm_sync_icache(unsigned long address, int len)
+{
+ int l;
+
+ for(l = 0; l < (len + 32); l += 32)
+ __ia64_fc(address + l);
+
+ ia64_sync_i();
+ ia64_srlz_i();
+}
+#endif
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+ fprintf(stderr,"pic_irq_request called!\n");
+}
+
+/* IPF hardware initialisation */
+static void ipf_init1(ram_addr_t ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename, int snapshot,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename,
+ int pci_enabled)
+{
+ char buf[1024];
+ int i;
+ ram_addr_t ram_addr, vga_ram_addr;
+ ram_addr_t above_4g_mem_size = 0;
+ PCIBus *pci_bus;
+ int piix3_devfn = -1;
+ CPUState *env;
+ NICInfo *nd;
+ qemu_irq *cpu_irq;
+ qemu_irq *i8259;
+ int page_size;
+
+ 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!\n");
+ exit(-1);
+ }
+
+ if (ram_size >= 0xc0000000 ) {
+ above_4g_mem_size = ram_size - 0xc0000000;
+ ram_size = 0xc0000000;
+ }
+
+ /* init CPUs */
+ for(i = 0; i < smp_cpus; i++) {
+ env = cpu_init();
+ if (i != 0)
+ env->hflags |= HF_HALTED_MASK;
+ register_savevm("cpu", i, 4, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+ }
+
+ /* allocate RAM */
+#ifdef USE_KVM
+#ifdef KVM_CAP_USER_MEMORY
+ if (kvm_allowed && kvm_qemu_check_extension(KVM_CAP_USER_MEMORY)) {
+ ram_addr = qemu_ram_alloc(0xa0000);
+ cpu_register_physical_memory(0, 0xa0000, ram_addr);
+ kvm_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);
+ kvm_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);
+ kvm_cpu_register_physical_memory(0x100000, ram_size - 0x100000,
+ ram_addr);
+ } else
+#endif
+#endif
+ {
+ ram_addr = qemu_ram_alloc(ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_addr);
+ }
+ /* allocate VGA RAM */
+ vga_ram_addr = qemu_ram_alloc(vga_ram_size);
+
+ /* 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);
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_cpu_register_physical_memory(0x100000000, above_4g_mem_size,
+ ram_addr);
+#endif
+ }
+
+ /*Load firware to its proper position.*/
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ int r;
+ unsigned long image_size;
+ char *image = NULL;
+ char *fw_image_start;
+ ram_addr_t fw_offset = qemu_ram_alloc(GFW_SIZE);
+ char *fw_start = phys_ram_base + fw_offset;
+
+ 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");
+ return ;
+ }
+ fw_image_start = fw_start + GFW_SIZE - image_size;
+
+#ifdef KVM_CAP_USER_MEMORY
+ r = kvm_qemu_check_extension(KVM_CAP_USER_MEMORY);
+ if (r) {
+ cpu_register_physical_memory(GFW_START, GFW_SIZE, fw_offset);
+ kvm_cpu_register_physical_memory(GFW_START,GFW_SIZE, fw_offset);
+ memcpy(fw_image_start, image, image_size);
+ }
+ else
+#endif
+ {
+ fw_start = kvm_create_phys_mem(kvm_context, (uint32_t)(-image_size),
+ image_size, 0, 1);
+ if (!fw_start)
+ exit(1);
+ fw_image_start = fw_start + GFW_SIZE - image_size;
+ memcpy(fw_image_start, image, image_size);
+ }
+ free(image);
+ kvm_sync_icache((unsigned long)fw_image_start, image_size);
+ kvm_ia64_build_hob(ram_size, smp_cpus, fw_start);
+ }
+#endif
+
+ cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1);
+ i8259 = 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,
+ ds, phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ } else {
+ isa_cirrus_vga_init(ds, phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ }
+ } else {
+ if (pci_enabled) {
+ pci_vga_init(pci_bus, ds, phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size, 0, 0);
+ } else {
+ isa_vga_init(ds, phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ }
+ }
+ rtc_state = rtc_init(0x70, i8259[8]);
+
+ 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]], 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++) {
+ nd = &nd_table[i];
+ if (!nd->model) {
+ if (pci_enabled) {
+ nd->model = "ne2k_pci";
+ } else {
+ nd->model = "ne2k_isa";
+ }
+ }
+ if (strcmp(nd->model, "ne2k_isa") == 0) {
+ pc_init_ne2k_isa(nd, i8259);
+ } else if (pci_enabled) {
+ if (strcmp(nd->model, "?") == 0)
+ fprintf(stderr, "qemu: Supported ISA NICs: ne2k_isa\n");
+ pci_nic_init(pci_bus, nd, -1);
+ } else if (strcmp(nd->model, "?") == 0) {
+ fprintf(stderr, "qemu: Supported ISA NICs: ne2k_isa\n");
+ exit(1);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+ exit(1);
+ }
+ }
+
+#undef USE_HYPERCALL // Disable hypercall now, due to shor of support for VT-i.
+#ifdef USE_HYPERCALL
+ pci_hypercall_init(pci_bus);
+#endif
+ if (pci_enabled) {
+ pci_piix3_ide_init(pci_bus, bs_table, piix3_devfn + 1, i8259);
+ } else {
+ for(i = 0; i < 2; i++) {
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], i8259[ide_irq[i]],
+ bs_table[2 * i], bs_table[2 * i + 1]);
+ }
+ }
+
+ i8042_init(i8259[1], i8259[12], 0x60);
+ DMA_init(0);
+#ifdef HAS_AUDIO
+ audio_init(pci_enabled ? pci_bus : NULL, i8259);
+#endif
+
+ floppy_controller = fdctrl_init(i8259[6], 2, 0, 0x3f0, fd_table);
+
+ /*Disable cmos support for ia64.*/
+ //cmos_init(ram_size, above_4g_mem_size, boot_device, bs_table, smp_cpus);
+
+ 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);
+ for (i = 0; i < 8; i++) {
+ smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256));
+ }
+ }
+
+ if (i440fx_state) {
+ i440fx_init_memory_mappings(i440fx_state);
+ }
+#if 0
+ /* ??? Need to figure out some way for the user to
+ specify SCSI devices. */
+ if (pci_enabled) {
+ void *scsi;
+ BlockDriverState *bdrv;
+
+ scsi = lsi_scsi_init(pci_bus, -1);
+ bdrv = bdrv_new("scsidisk");
+ bdrv_open(bdrv, "scsi_disk.img", 0);
+ lsi_scsi_attach(scsi, bdrv, -1);
+ bdrv = bdrv_new("scsicd");
+ bdrv_open(bdrv, "scsi_cd.iso", 0);
+ bdrv_set_type_hint(bdrv, BDRV_TYPE_CDROM);
+ lsi_scsi_attach(scsi, bdrv, -1);
+ }
+#endif
+}
+
+static void ipf_init_pci(ram_addr_t ram_size, int vga_ram_size, int boot_device,
+ DisplayState *ds, const char **fd_filename,
+ int snapshot,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ ipf_init1(ram_size, vga_ram_size, boot_device,
+ ds, fd_filename, snapshot,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, 1);
+}
+
+QEMUMachine ipf_machine = {
+ "itanium",
+ "Itanium Platform",
+ ipf_init_pci,
+};
diff --git a/hw/ne2000.c b/hw/ne2000.c
index 44f30c2af..63bb3e2f1 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -146,6 +146,8 @@ typedef struct NE2000State {
uint8_t mem[NE2000_MEM_SIZE];
} NE2000State;
+static int ne2000_id;
+
static void ne2000_reset(NE2000State *s)
{
int i;
@@ -753,7 +755,7 @@ void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd)
s->macaddr[4],
s->macaddr[5]);
- register_savevm("ne2000", 0, 2, ne2000_save, ne2000_load, s);
+ register_savevm("ne2000", ne2000_id++, 2, ne2000_save, ne2000_load, s);
}
/***********************************************************/
@@ -824,5 +826,5 @@ void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn)
s->macaddr[5]);
/* XXX: instance number ? */
- register_savevm("ne2000", 0, 3, ne2000_save, ne2000_load, s);
+ register_savevm("ne2000", ne2000_id++, 3, ne2000_save, ne2000_load, s);
}
diff --git a/hw/pc.c b/hw/pc.c
index b4f0db7f5..dde40c369 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -32,6 +32,11 @@
#include "smbus.h"
#include "boards.h"
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+extern int kvm_allowed;
+#endif
+
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@@ -181,7 +186,9 @@ static int boot_device2nibble(char boot_device)
}
/* hd_table must contain 4 block drivers */
-static void cmos_init(int ram_size, const char *boot_device, BlockDriverState **hd_table)
+static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device, BlockDriverState **hd_table,
+ int smp_cpus)
{
RTCState *s = rtc_state;
int nbds, bds[3] = { 0, };
@@ -204,6 +211,13 @@ static void cmos_init(int ram_size, const char *boot_device, BlockDriverState **
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
@@ -696,8 +710,13 @@ static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic)
nb_ne2k++;
}
+#ifdef USE_KVM
+extern kvm_context_t kvm_context;
+extern int kvm_allowed;
+#endif
+
/* PC hardware initialisation */
-static void pc_init1(int ram_size, int vga_ram_size,
+static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename,
@@ -706,6 +725,7 @@ static void pc_init1(int ram_size, int vga_ram_size,
char buf[1024];
int ret, linux_boot, i;
ram_addr_t ram_addr, vga_ram_addr, bios_offset, vga_bios_offset;
+ ram_addr_t above_4g_mem_size = 0;
int bios_size, isa_bios_size, vga_bios_size;
PCIBus *pci_bus;
int piix3_devfn = -1;
@@ -717,6 +737,11 @@ static void pc_init1(int ram_size, int vga_ram_size,
BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
BlockDriverState *fd[MAX_FD];
+ if (ram_size >= 0xe0000000 ) {
+ above_4g_mem_size = ram_size - 0xe0000000;
+ ram_size = 0xe0000000;
+ }
+
linux_boot = (kernel_filename != NULL);
/* init CPUs */
@@ -749,9 +774,25 @@ static void pc_init1(int ram_size, int vga_ram_size,
}
/* allocate RAM */
- ram_addr = qemu_ram_alloc(ram_size);
- cpu_register_physical_memory(0, ram_size, ram_addr);
-
+#ifdef USE_KVM
+ #ifdef KVM_CAP_USER_MEMORY
+ if (kvm_allowed && kvm_qemu_check_extension(KVM_CAP_USER_MEMORY)) {
+ ram_addr = qemu_ram_alloc(0xa0000);
+ cpu_register_physical_memory(0, 0xa0000, ram_addr);
+ kvm_cpu_register_physical_memory(0, 0xa0000, ram_addr);
+
+ ram_addr = qemu_ram_alloc(0x100000 - 0xa0000); // hole
+ ram_addr = qemu_ram_alloc(ram_size - 0x100000);
+ cpu_register_physical_memory(0x100000, ram_size - 0x100000, ram_addr);
+ kvm_cpu_register_physical_memory(0x100000, ram_size - 0x100000,
+ ram_addr);
+ } else
+ #endif
+#endif
+ {
+ ram_addr = qemu_ram_alloc(ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_addr);
+ }
/* allocate VGA RAM */
vga_ram_addr = qemu_ram_alloc(vga_ram_size);
@@ -790,9 +831,25 @@ static void pc_init1(int ram_size, int vga_ram_size,
exit(1);
}
+ /* 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);
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_cpu_register_physical_memory(0x100000000, above_4g_mem_size,
+ ram_addr);
+#endif
+ }
+
/* setup basic memory access */
cpu_register_physical_memory(0xc0000, 0x10000,
vga_bios_offset | IO_MEM_ROM);
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_cpu_register_physical_memory(0xc0000, 0x10000,
+ vga_bios_offset | IO_MEM_ROM);
+#endif
/* map the last 128KB of the BIOS in ISA space */
isa_bios_size = bios_size;
@@ -803,6 +860,13 @@ static void pc_init1(int ram_size, int vga_ram_size,
cpu_register_physical_memory(0x100000 - isa_bios_size,
isa_bios_size,
(bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_cpu_register_physical_memory(0x100000 - isa_bios_size,
+ isa_bios_size,
+ (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
+#endif
+
{
ram_addr_t option_rom_offset;
@@ -828,6 +892,13 @@ static void pc_init1(int ram_size, int vga_ram_size,
size = (size + 4095) & ~4095;
cpu_register_physical_memory(0xd0000 + offset,
size, option_rom_offset | IO_MEM_ROM);
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_cpu_register_physical_memory(0xd0000 + offset,
+ size, option_rom_offset |
+ IO_MEM_ROM);
+#endif
+
offset += size;
}
}
@@ -835,6 +906,25 @@ static void pc_init1(int ram_size, int vga_ram_size,
/* map all the bios at the top of memory */
cpu_register_physical_memory((uint32_t)(-bios_size),
bios_size, bios_offset | IO_MEM_ROM);
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ int r;
+#ifdef KVM_CAP_USER_MEMORY
+ r = kvm_qemu_check_extension(KVM_CAP_USER_MEMORY);
+ if (r)
+ kvm_cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+ else
+#endif
+ {
+ bios_mem = kvm_create_phys_mem(kvm_context, (uint32_t)(-bios_size),
+ bios_size, 0, 1);
+ if (!bios_mem)
+ exit(1);
+ memcpy(bios_mem, phys_ram_base + bios_offset, bios_size);
+ }
+ }
+#endif
bochs_bios_init();
@@ -933,6 +1023,11 @@ static void pc_init1(int ram_size, int vga_ram_size,
}
}
+#define USE_HYPERCALL
+#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);
@@ -970,7 +1065,7 @@ static void pc_init1(int ram_size, int vga_ram_size,
}
floppy_controller = fdctrl_init(i8259[6], 2, 0, 0x3f0, fd);
- cmos_init(ram_size, boot_device, hd);
+ cmos_init(ram_size, above_4g_mem_size, boot_device, hd, smp_cpus);
if (pci_enabled && usb_enabled) {
usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
@@ -1010,7 +1105,7 @@ static void pc_init1(int ram_size, int vga_ram_size,
}
}
-static void pc_init_pci(int ram_size, int vga_ram_size,
+static void pc_init_pci(ram_addr_t ram_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
const char *kernel_filename,
const char *kernel_cmdline,
@@ -1022,7 +1117,7 @@ static void pc_init_pci(int ram_size, int vga_ram_size,
initrd_filename, 1, cpu_model);
}
-static void pc_init_isa(int ram_size, int vga_ram_size,
+static void pc_init_isa(ram_addr_t ram_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
const char *kernel_filename,
const char *kernel_cmdline,
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 2c6a6984b..9430a8ca0 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3415,6 +3415,7 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
PCIRTL8139State *d;
RTL8139State *s;
uint8_t *pci_conf;
+ static int rtl8139_id;
d = (PCIRTL8139State *)pci_register_device(bus,
"RTL8139", sizeof(PCIRTL8139State),
@@ -3465,7 +3466,7 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
s->cplus_txbuffer_offset = 0;
/* XXX: instance number ? */
- register_savevm("rtl8139", 0, 3, rtl8139_save, rtl8139_load, s);
+ register_savevm("rtl8139", rtl8139_id++, 3, rtl8139_save, rtl8139_load, s);
#if RTL8139_ONBOARD_TIMER
s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index b55fd849a..12afadeea 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -151,7 +151,7 @@ static void uhci_reset(UHCIState *s)
}
}
-#if 0
+#if 1
static void uhci_save(QEMUFile *f, void *opaque)
{
UHCIState *s = opaque;
@@ -950,4 +950,6 @@ void usb_uhci_piix4_init(PCIBus *bus, int devfn)
to rely on this. */
pci_register_io_region(&s->dev, 4, 0x20,
PCI_ADDRESS_SPACE_IO, uhci_map);
+
+ register_savevm("uhci", 0, 1, uhci_save, uhci_load, s);
}
diff --git a/hw/vga.c b/hw/vga.c
index 70b7c6d45..593f4c6bb 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -28,6 +28,8 @@
#include "vga_int.h"
#include "pixel_ops.h"
+#include <sys/mman.h>
+
//#define DEBUG_VGA
//#define DEBUG_VGA_MEM
//#define DEBUG_VGA_REG
@@ -1412,18 +1414,57 @@ void vga_invalidate_scanlines(VGAState *s, int y1, int y2)
}
}
+#ifdef USE_KVM
+
+#include "libkvm.h"
+extern kvm_context_t kvm_context;
+
+static int bitmap_get_dirty(unsigned long *bitmap, unsigned nr)
+{
+ unsigned word = nr / ((sizeof bitmap[0]) * 8);
+ unsigned bit = nr % ((sizeof bitmap[0]) * 8);
+
+ //printf("%x -> %ld\n", nr, (bitmap[word] >> bit) & 1);
+ return (bitmap[word] >> bit) & 1;
+}
+
+#endif
+
+#ifdef USE_KVM
+extern int kvm_allowed;
+#endif
+
/*
* graphic modes
*/
static void vga_draw_graphic(VGAState *s, int full_update)
{
- int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask;
- int width, height, shift_control, line_offset, page0, page1, bwidth;
+ int y1, y, update, linesize, y_start, double_scan, mask;
+ int width, height, shift_control, line_offset, bwidth;
int disp_width, multi_scan, multi_run;
uint8_t *d;
uint32_t v, addr1, addr;
+ long page0, page1, page_min, page_max;
vga_draw_line_func *vga_draw_line;
+#ifdef USE_KVM
+
+ /* HACK ALERT */
+#define BITMAP_SIZE ((8*1024*1024) / 4096 / 8 / sizeof(long))
+ unsigned long bitmap[BITMAP_SIZE];
+#ifndef TARGET_IA64
+ int r;
+ if (kvm_allowed) {
+ r = kvm_get_dirty_pages(kvm_context, s->map_addr, &bitmap);
+ if (r < 0)
+ fprintf(stderr, "kvm: get_dirty_pages returned %d\n", r);
+ }
+#else
+ memset(bitmap, 0xff, BITMAP_SIZE*sizeof(long));
+ //FIXME:Always flush full screen before log dirty ready!!
+#endif
+#endif
+
full_update |= update_basic_params(s);
s->get_resolution(s, &width, &height);
@@ -1530,10 +1571,20 @@ static void vga_draw_graphic(VGAState *s, int full_update)
update = full_update |
cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ update |= bitmap_get_dirty(bitmap, (page0 - s->vram_offset) >> TARGET_PAGE_BITS);
+ update |= bitmap_get_dirty(bitmap, (page1 - s->vram_offset) >> TARGET_PAGE_BITS);
+ }
+#endif
if ((page1 - page0) > TARGET_PAGE_SIZE) {
/* if wide line, can use another page */
update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
VGA_DIRTY_FLAG);
+#ifdef USE_KVM
+ if (kvm_allowed)
+ update |= bitmap_get_dirty(bitmap, (page0 - s->vram_offset) >> TARGET_PAGE_BITS);
+#endif
}
/* explicit invalidation for the hardware cursor */
update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
@@ -1787,9 +1838,176 @@ 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);
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ unsigned long vga_ram_begin, vga_ram_end;
+ void *vram_pointer, *old_vram;
+
+ vga_ram_begin = addr;
+ vga_ram_end = addr + VGA_RAM_SIZE;
+
+ if (vga_ram_begin == s->map_addr &&
+ vga_ram_end == s->map_end) {
+ return;
+ }
+
+ if (s->map_addr && s->map_end)
+ unset_vram_mapping(s->map_addr, s->map_end);
+
+ vram_pointer = set_vram_mapping(vga_ram_begin, vga_ram_end);
+ if (!vram_pointer) {
+ fprintf(stderr, "set_vram_mapping failed\n");
+ s->map_addr = s->map_end = 0;
+ }
+ else {
+ fprintf(stderr, "vram_pointer=%p\n", vram_pointer);
+ old_vram = vga_update_vram((VGAState *)s, vram_pointer,
+ VGA_RAM_SIZE);
+ if (s->map_addr && s->map_end)
+ munmap(old_vram, s->map_end - s->map_addr);
+ else
+ qemu_free(old_vram);
+ s->map_addr = vga_ram_begin;
+ s->map_end = vga_ram_end;
+ }
+ }
+#endif
}
}
+#ifdef TARGET_IA64
+/* do the same job as vgabios before vgabios get ready - yeah */
+void vga_bios_init(VGAState *s)
+{
+ uint8_t palette_model[192] = {
+ 0, 0, 0, 0, 0, 170, 0, 170,
+ 0, 0, 170, 170, 170, 0, 0, 170,
+ 0, 170, 170, 85, 0, 170, 170, 170,
+ 85, 85, 85, 85, 85, 255, 85, 255,
+ 85, 85, 255, 255, 255, 85, 85, 255,
+ 85, 255, 255, 255, 85, 255, 255, 255,
+ 0, 21, 0, 0, 21, 42, 0, 63,
+ 0, 0, 63, 42, 42, 21, 0, 42,
+ 21, 42, 42, 63, 0, 42, 63, 42,
+ 0, 21, 21, 0, 21, 63, 0, 63,
+ 21, 0, 63, 63, 42, 21, 21, 42,
+ 21, 63, 42, 63, 21, 42, 63, 63,
+ 21, 0, 0, 21, 0, 42, 21, 42,
+ 0, 21, 42, 42, 63, 0, 0, 63,
+ 0, 42, 63, 42, 0, 63, 42, 42,
+ 21, 0, 21, 21, 0, 63, 21, 42,
+ 21, 21, 42, 63, 63, 0, 21, 63,
+ 0, 63, 63, 42, 21, 63, 42, 63,
+ 21, 21, 0, 21, 21, 42, 21, 63,
+ 0, 21, 63, 42, 63, 21, 0, 63,
+ 21, 42, 63, 63, 0, 63, 63, 42,
+ 21, 21, 21, 21, 21, 63, 21, 63,
+ 21, 21, 63, 63, 63, 21, 21, 63,
+ 21, 63, 63, 63, 21, 63, 63, 63
+ };
+
+ s->latch = 0;
+
+ s->sr_index = 3;
+ s->sr[0] = 3;
+ s->sr[1] = 0;
+ s->sr[2] = 3;
+ s->sr[3] = 0;
+ s->sr[4] = 2;
+ s->sr[5] = 0;
+ s->sr[6] = 0;
+ s->sr[7] = 0;
+
+ s->gr_index = 5;
+ s->gr[0] = 0;
+ s->gr[1] = 0;
+ s->gr[2] = 0;
+ s->gr[3] = 0;
+ s->gr[4] = 0;
+ s->gr[5] = 16;
+ s->gr[6] = 14;
+ s->gr[7] = 15;
+ s->gr[8] = 255;
+
+ /* changed by out 0x03c0 */
+ s->ar_index = 32;
+ s->ar[0] = 0;
+ s->ar[1] = 1;
+ s->ar[2] = 2;
+ s->ar[3] = 3;
+ s->ar[4] = 4;
+ s->ar[5] = 5;
+ s->ar[6] = 6;
+ s->ar[7] = 7;
+ s->ar[8] = 8;
+ s->ar[9] = 9;
+ s->ar[10] = 10;
+ s->ar[11] = 11;
+ s->ar[12] = 12;
+ s->ar[13] = 13;
+ s->ar[14] = 14;
+ s->ar[15] = 15;
+ s->ar[16] = 12;
+ s->ar[17] = 0;
+ s->ar[18] = 15;
+ s->ar[19] = 8;
+ s->ar[20] = 0;
+
+ s->ar_flip_flop = 1;
+
+ s->cr_index = 15;
+ s->cr[0] = 95;
+ s->cr[1] = 79;
+ s->cr[2] = 80;
+ s->cr[3] = 130;
+ s->cr[4] = 85;
+ s->cr[5] = 129;
+ s->cr[6] = 191;
+ s->cr[7] = 31;
+ s->cr[8] = 0;
+ s->cr[9] = 79;
+ s->cr[10] = 14;
+ s->cr[11] = 15;
+ s->cr[12] = 0;
+ s->cr[13] = 0;
+ s->cr[14] = 5;
+ s->cr[15] = 160;
+ s->cr[16] = 156;
+ s->cr[17] = 142;
+ s->cr[18] = 143;
+ s->cr[19] = 40;
+ s->cr[20] = 31;
+ s->cr[21] = 150;
+ s->cr[22] = 185;
+ s->cr[23] = 163;
+ s->cr[24] = 255;
+
+ s->msr = 103;
+ s->fcr = 0;
+ s->st00 = 0;
+ s->st01 = 0;
+
+ /* dac_* & palette will be initialized by os through out 0x03c8 &
+ * out 0c03c9(1:3) */
+ s->dac_state = 0;
+ s->dac_sub_index = 0;
+ s->dac_read_index = 0;
+ s->dac_write_index = 16;
+ s->dac_cache[0] = 255;
+ s->dac_cache[1] = 255;
+ s->dac_cache[2] = 255;
+
+ /* palette */
+ memcpy(s->palette, palette_model, 192);
+
+ s->bank_offset = 0;
+ s->graphic_mode = -1;
+
+ /* TODO: add vbe support if enabled */
+}
+#endif
+
+/* when used on xen/kvm environment, the vga_ram_base is not used */
void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
unsigned long vga_ram_offset, int vga_ram_size)
{
@@ -1820,7 +2038,14 @@ void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
vga_reset(s);
+#ifndef USE_KVM
s->vram_ptr = vga_ram_base;
+#else
+ if (kvm_allowed)
+ s->vram_ptr = qemu_malloc(vga_ram_size);
+ else
+ s->vram_ptr = vga_ram_base;
+#endif
s->vram_offset = vga_ram_offset;
s->vram_size = vga_ram_size;
s->ds = ds;
@@ -1830,6 +2055,9 @@ void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
s->update = vga_update_display;
s->invalidate = vga_invalidate_display;
s->screen_dump = vga_screen_dump;
+#ifdef TARGET_IA64
+ vga_bios_init(s);
+#endif
}
/* used by both ISA and PCI */
@@ -2053,6 +2281,31 @@ int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
return 0;
}
+void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size)
+{
+ uint8_t *old_pointer;
+
+ if (s->vram_size != vga_ram_size) {
+ fprintf(stderr, "No support to change vga_ram_size\n");
+ return NULL;
+ }
+
+ if (!vga_ram_base) {
+ vga_ram_base = qemu_malloc(vga_ram_size);
+ if (!vga_ram_base) {
+ fprintf(stderr, "reallocate error\n");
+ return NULL;
+ }
+ }
+
+ /* XXX lock needed? */
+ memcpy(vga_ram_base, s->vram_ptr, vga_ram_size);
+ old_pointer = s->vram_ptr;
+ s->vram_ptr = vga_ram_base;
+
+ return old_pointer;
+}
+
/********************************************************/
/* vga screen dump */
diff --git a/hw/vga_int.h b/hw/vga_int.h
index a94162d33..f08700ef8 100644
--- a/hw/vga_int.h
+++ b/hw/vga_int.h
@@ -79,6 +79,14 @@
#define CH_ATTR_SIZE (160 * 100)
#define VGA_MAX_HEIGHT 2048
+#ifdef USE_KVM
+#define VGA_KVM_STATE \
+ unsigned long map_addr; \
+ unsigned long map_end;
+#else
+#define VGA_KVM_STATE
+#endif
+
#define VGA_STATE_COMMON \
uint8_t *vram_ptr; \
unsigned long vram_offset; \
@@ -145,11 +153,21 @@
void (*cursor_draw_line)(struct VGAState *s, uint8_t *d, int y); \
/* tell for each page if it has been updated since the last time */ \
uint32_t last_palette[256]; \
- uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
+ uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ \
+ VGA_KVM_STATE
typedef struct VGAState {
VGA_STATE_COMMON
+
+#ifdef USE_KVM
+ int32_t aliases_enabled;
+ int32_t pad1;
+ uint32_t aliased_bank_base[2];
+ uint32_t aliased_bank_limit[2];
+#endif
+
+
} VGAState;
static inline int c6_to_8(int v)
@@ -182,5 +200,12 @@ void vga_draw_cursor_line_32(uint8_t *d1, const uint8_t *src1,
unsigned int color0, unsigned int color1,
unsigned int color_xor);
+#ifdef USE_KVM
+/* let kvm create vga memory */
+void *set_vram_mapping(unsigned long begin, unsigned long end);
+int unset_vram_mapping(unsigned long begin, unsigned long end);
+#endif
+
+void *vga_update_vram(VGAState *s, void *vga_ram_base, int vga_ram_size);
extern const uint8_t sr_mask[8];
extern const uint8_t gr_mask[16];
diff --git a/hw/vmport.c b/hw/vmport.c
index 8044c9fe6..c2253080a 100644
--- a/hw/vmport.c
+++ b/hw/vmport.c
@@ -21,10 +21,13 @@
* 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 "libkvm.h"
+#include "qemu-kvm.h"
#define VMPORT_CMD_GETVERSION 0x0a
#define VMPORT_CMD_GETRAMSIZE 0x14
@@ -55,6 +58,18 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
VMPortState *s = opaque;
unsigned char command;
uint32_t eax;
+ uint32_t ret;
+
+#ifdef USE_KVM
+ struct kvm_regs regs;
+ extern kvm_context_t kvm_context;
+ if (kvm_allowed) {
+ kvm_get_regs(kvm_context, s->env->cpu_index, &regs);
+ s->env->regs[R_EAX] = regs.rax; s->env->regs[R_EBX] = regs.rbx;
+ s->env->regs[R_ECX] = regs.rcx; s->env->regs[R_EDX] = regs.rdx;
+ s->env->regs[R_ESI] = regs.rsi; s->env->regs[R_EDI] = regs.rdi;
+ }
+#endif
eax = s->env->regs[R_EAX];
if (eax != VMPORT_MAGIC)
@@ -69,7 +84,18 @@ 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);
+
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ regs.rax = s->env->regs[R_EAX]; regs.rbx = s->env->regs[R_EBX];
+ regs.rcx = s->env->regs[R_ECX]; regs.rdx = s->env->regs[R_EDX];
+ regs.rsi = s->env->regs[R_ESI]; regs.rdi = s->env->regs[R_EDI];
+ kvm_set_regs(kvm_context, s->env->cpu_index, &regs);
+ }
+#endif
+
+ return ret;
}
static uint32_t vmport_cmd_get_version(void *opaque, uint32_t addr)
diff --git a/ia64.ld b/ia64.ld
index 8d2ede2d3..23c940d2b 100644
--- a/ia64.ld
+++ b/ia64.ld
@@ -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 */
diff --git a/kvm/.gitignore b/kvm/.gitignore
new file mode 100644
index 000000000..600508a18
--- /dev/null
+++ b/kvm/.gitignore
@@ -0,0 +1,29 @@
+*.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
+*.ko
+*.mod.c
+kernel/kvm_main.c
+kernel/kvm.h
+kernel/kvm_svm.h
+kernel/vmx.[ch]
+kernel/svm.[ch]
+kernel/mmu.c
+kernel/paging_tmpl.h
+kernel/segment_descriptor.h
+kernel/x86_emulate.[ch]
+kernel/include/linux/kvm*.h
+kernel/Module.symvers
+kernel/Modules.symvers
+kernel/.tmp_versions \ No newline at end of file
diff --git a/kvm/Makefile b/kvm/Makefile
new file mode 100644
index 000000000..122732f12
--- /dev/null
+++ b/kvm/Makefile
@@ -0,0 +1,74 @@
+
+include config.mak
+
+DESTDIR=
+
+rpmrelease = devel
+
+.PHONY: kernel user libkvm qemu bios clean
+
+all: $(if $(WANT_MODULE), kernel) user libkvm qemu
+
+kcmd = $(if $(WANT_MODULE),,@\#)
+
+qemu kernel user libkvm:
+ $(MAKE) -C $@
+
+qemu: libkvm
+user: libkvm
+
+bios:
+ $(MAKE) -C $@
+ cp bios/BIOS-bochs-latest qemu/pc-bios/bios.bin
+
+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
+ 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; do \
+ make -C $$i clean; \
+ done
+ rm -f config.mak user/config.mak
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..8ec22b8b1
--- /dev/null
+++ b/kvm/bios/Makefile
@@ -0,0 +1,116 @@
+# 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
+
+LDFLAGS =
+LIBS = -lm
+RANLIB = ranlib
+
+BCC = bcc
+GCC = gcc -m32
+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
+
+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 tmp.bin -u- -w- -g -0 -j -O -l rombiosl.txt
+ -perl ${srcdir}/makesym.perl < rombiosl.txt > rombiosl.sym
+ mv tmp.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 rombios32.ld
+ ld -o $@ -T rombios32.ld rombios32start.o rombios32.o
+
+rombios32.o: rombios32.c acpi-dsdt.hex
+ $(GCC) -m32 -O2 -Wall -c -o $@ $<
+
+acpi-dsdt.hex: acpi-dsdt.dsl
+ iasl -tc -p $@ $<
+
+rombios32start.o: rombios32start.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..3e332aeb2
--- /dev/null
+++ b/kvm/bios/Makefile.in
@@ -0,0 +1,117 @@
+# 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
+
+
+@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
+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
+ $(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 tmp.bin -u- -w- -g -0 -j -O -l rombiosl.txt
+ -perl ${srcdir}/makesym.perl < rombiosl.txt > rombiosl.sym
+ mv tmp.bin $@
+ ./biossums $@
+ @RMCOMMAND@ _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
+ @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 rombios32.ld rombios32start.o rombios32.o
+
+rombios32.o: rombios32.c acpi-dsdt.hex
+ $(GCC) -O2 -Wall -c -o $@ $<
+
+ifeq ("1", "0")
+acpi-dsdt.hex: acpi-dsdt.dsl
+ iasl -tc -p $@ $<
+endif
+
+rombios32start.o: rombios32start.S
+ $(GCC) -c -o $@ $<
+
+BIOS-bochs-latest: rombios16.bin rombios32.bin
+ cat rombios32.bin rombios16.bin > $@
+
+biossums: biossums.c
+ $(GCC) -o biossums biossums.c
diff --git a/kvm/bios/VGABIOS-lgpl-README b/kvm/bios/VGABIOS-lgpl-README
new file mode 100644
index 000000000..3b585e66f
--- /dev/null
+++ b/kvm/bios/VGABIOS-lgpl-README
@@ -0,0 +1,214 @@
+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.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/bios/VGABIOS-lgpl-latest b/kvm/bios/VGABIOS-lgpl-latest
new file mode 100644
index 000000000..1e0e5e1f8
--- /dev/null
+++ b/kvm/bios/VGABIOS-lgpl-latest
Binary files differ
diff --git a/kvm/bios/VGABIOS-lgpl-latest-cirrus b/kvm/bios/VGABIOS-lgpl-latest-cirrus
new file mode 100644
index 000000000..c129b71c0
--- /dev/null
+++ b/kvm/bios/VGABIOS-lgpl-latest-cirrus
Binary files differ
diff --git a/kvm/bios/VGABIOS-lgpl-latest-cirrus-debug b/kvm/bios/VGABIOS-lgpl-latest-cirrus-debug
new file mode 100644
index 000000000..4e73c51ef
--- /dev/null
+++ b/kvm/bios/VGABIOS-lgpl-latest-cirrus-debug
Binary files differ
diff --git a/kvm/bios/VGABIOS-lgpl-latest-debug b/kvm/bios/VGABIOS-lgpl-latest-debug
new file mode 100644
index 000000000..a1ea7f73d
--- /dev/null
+++ b/kvm/bios/VGABIOS-lgpl-latest-debug
Binary files differ
diff --git a/kvm/bios/acpi-dsdt.dsl b/kvm/bios/acpi-dsdt.dsl
new file mode 100755
index 000000000..df255ce21
--- /dev/null
+++ b/kvm/bios/acpi-dsdt.dsl
@@ -0,0 +1,570 @@
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+DefinitionBlock (
+ "acpi-dsdt.aml", // Output Filename
+ "DSDT", // Signature
+ 0x01, // DSDT Compliance Revision
+ "BXPC", // OEMID
+ "BXDSDT", // TABLE ID
+ 0x1 // OEM Revision
+ )
+{
+ Scope (_PR)
+ {
+ Processor (CPU0, 0x00, 0x0000b010, 0x06) {}
+ Processor (CPU1, 0x01, 0x0000b010, 0x06) {}
+ Processor (CPU2, 0x02, 0x0000b010, 0x06) {}
+ Processor (CPU3, 0x03, 0x0000b010, 0x06) {}
+ Processor (CPU4, 0x04, 0x0000b010, 0x06) {}
+ Processor (CPU5, 0x05, 0x0000b010, 0x06) {}
+ Processor (CPU6, 0x06, 0x0000b010, 0x06) {}
+ Processor (CPU7, 0x07, 0x0000b010, 0x06) {}
+ Processor (CPU8, 0x08, 0x0000b010, 0x06) {}
+ Processor (CPU9, 0x09, 0x0000b010, 0x06) {}
+ Processor (CPUA, 0x0a, 0x0000b010, 0x06) {}
+ Processor (CPUB, 0x0b, 0x0000b010, 0x06) {}
+ Processor (CPUC, 0x0c, 0x0000b010, 0x06) {}
+ Processor (CPUD, 0x0d, 0x0000b010, 0x06) {}
+ Processor (CPUE, 0x0e, 0x0000b010, 0x06) {}
+ }
+
+ Scope (\)
+ {
+ /* CMOS memory access */
+ OperationRegion (CMS, SystemIO, 0x70, 0x02)
+ Field (CMS, ByteAcc, NoLock, Preserve)
+ {
+ CMSI, 8,
+ CMSD, 8
+ }
+ Method (CMRD, 1, NotSerialized)
+ {
+ Store (Arg0, CMSI)
+ Store (CMSD, Local0)
+ Return (Local0)
+ }
+
+ /* 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 */
+
+ // PCI Slot 0
+ Package() {0x0000ffff, 0, LNKD, 0},
+ Package() {0x0000ffff, 1, LNKA, 0},
+ Package() {0x0000ffff, 2, LNKB, 0},
+ Package() {0x0000ffff, 3, LNKC, 0},
+
+ // PCI Slot 1
+ Package() {0x0001ffff, 0, LNKA, 0},
+ Package() {0x0001ffff, 1, LNKB, 0},
+ Package() {0x0001ffff, 2, LNKC, 0},
+ Package() {0x0001ffff, 3, LNKD, 0},
+
+ // PCI Slot 2
+ Package() {0x0002ffff, 0, LNKB, 0},
+ Package() {0x0002ffff, 1, LNKC, 0},
+ Package() {0x0002ffff, 2, LNKD, 0},
+ Package() {0x0002ffff, 3, LNKA, 0},
+
+ // PCI Slot 3
+ Package() {0x0003ffff, 0, LNKC, 0},
+ Package() {0x0003ffff, 1, LNKD, 0},
+ Package() {0x0003ffff, 2, LNKA, 0},
+ Package() {0x0003ffff, 3, LNKB, 0},
+
+ // PCI Slot 4
+ Package() {0x0004ffff, 0, LNKD, 0},
+ Package() {0x0004ffff, 1, LNKA, 0},
+ Package() {0x0004ffff, 2, LNKB, 0},
+ Package() {0x0004ffff, 3, LNKC, 0},
+
+ // PCI Slot 5
+ Package() {0x0005ffff, 0, LNKA, 0},
+ Package() {0x0005ffff, 1, LNKB, 0},
+ Package() {0x0005ffff, 2, LNKC, 0},
+ Package() {0x0005ffff, 3, LNKD, 0},
+ })
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (MEMP, 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, MinNotFixed, MaxFixed, NonCacheable, ReadWrite,
+ 0x00000000, // Address Space Granularity
+ 0x00000000, // Address Range Minimum
+ 0xFEBFFFFF, // Address Range Maximum
+ 0x00000000, // Address Translation Offset
+ 0x00000000, // Address Length
+ ,, MEMF, AddressRangeMemory, TypeStatic)
+ })
+ CreateDWordField (MEMP, \_SB.PCI0._CRS.MEMF._MIN, PMIN)
+ CreateDWordField (MEMP, \_SB.PCI0._CRS.MEMF._MAX, PMAX)
+ CreateDWordField (MEMP, \_SB.PCI0._CRS.MEMF._LEN, PLEN)
+ /* compute available RAM */
+ Add(CMRD(0x34), ShiftLeft(CMRD(0x35), 8), Local0)
+ ShiftLeft(Local0, 16, Local0)
+ Add(Local0, 0x1000000, Local0)
+ /* update field of last region */
+ Store(Local0, PMIN)
+ Subtract (PMAX, PMIN, PLEN)
+ Increment (PLEN)
+ Return (MEMP)
+ }
+ }
+ }
+
+ Scope(\_SB.PCI0) {
+
+ /* PIIX3 ISA bridge */
+ Device (ISA) {
+ Name (_ADR, 0x00010000)
+
+ /* PIIX PCI to ISA irq remapping */
+ OperationRegion (P40C, PCI_Config, 0x60, 0x04)
+
+
+ /* 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, 9, 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, 9, 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, 9, 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, 9, 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)
+ }
+ }
+ }
+
+ /* S5 = power off state */
+ Name (_S5, Package (4) {
+ 0x00, // PM1a_CNT.SLP_TYP
+ 0x00, // PM2a_CNT.SLP_TYP
+ 0x00, // reserved
+ 0x00, // reserved
+ })
+}
diff --git a/kvm/bios/apmbios.S b/kvm/bios/apmbios.S
new file mode 100644
index 000000000..a79cfb92c
--- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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..df604e2ae
--- /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..a9b565317
--- /dev/null
+++ b/kvm/bios/rombios.c
@@ -0,0 +1,10979 @@
+/////////////////////////////////////////////////////////////////////////
+// $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)
+
+ // 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
+
+ // 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
+ 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
+ }
+
+ // 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
+ 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
+ }
+
+ // 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
+ cmp cx, #0x0000
+ 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]
+ inc bx
+ inc bx
+ 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
+ inc bx
+ inc bx
+ 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
+ add 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
+ add 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
+ add eax, ebx
+ shr ebx, #16
+ test eax, eax
+ ret
+
+ ;; sr function
+ lsrul:
+ mov cx,di
+ jcxz lsr_exit
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ add 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
+ add 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
+ add 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 lba_capable; // LBA capable flag - always yes for bochs devices
+ Bit8u mode; // transfert 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; // Total sectors count
+ } 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 0x9fc00, and the boot
+ // device tables are at 0x9ff00 -- 0x9ffff
+ 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_segment;
+ Bit16u dpte_offset;
+ 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;
+
+
+
+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 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 }, /* \| */
+ { 0x5700, 0x5700, none, none, none }, /* F11 */
+ { 0x5800, 0x5800, none, none, 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, s)
+ Bit16u action;
+ Bit8u *s;
+{
+ Bit8u c;
+ if (!s)
+ s = "<NULL>";
+
+ while (c = read_byte(get_CS(), s)) {
+ send(action, c);
+ s++;
+ }
+}
+
+//--------------------------------------------------------------------------
+// bios_printf()
+// A compact variable argument printf function which prints its output via
+// an I/O port so that it can be logged by Bochs/Plex.
+// Currently, only %x is supported (or %02x, %04x, etc).
+//
+// Supports %[format_width][format]
+// where format can be d,x,c,s
+//--------------------------------------------------------------------------
+ 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;
+
+ 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') {
+ if (format_width == 0)
+ format_width = 4;
+ for (i=format_width-1; i>=0; i--) {
+ nibble = (arg >> (4 * i)) & 0x000f;
+ send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
+ }
+ }
+ else if (c == 'u') {
+ put_uint(action, arg, format_width, 0);
+ }
+ else if (c == 'l') {
+ s++;
+ arg_ptr++; /* increment to next arg */
+ hibyte = read_word(arg_seg, arg_ptr);
+ put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
+ }
+ 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, 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);
+}
+
+//--------------------------------------------------------------------------
+// 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
+//--------------------------------------------------------------------------
+
+/* 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 */
+
+struct ipl_entry {
+ Bit16u type;
+ Bit16u flags;
+ Bit32u vector;
+ Bit32u description;
+ Bit32u reserved;
+};
+
+static void
+init_boot_vectors()
+{
+ struct ipl_entry e;
+ Bit16u count = 0;
+ Bit16u ss = get_SS();
+
+ /* Clear out the IPL table. */
+ memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, 0xff);
+
+ /* Floppy drive */
+ e.type = 1; 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 = 2; 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 = 3; 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; struct ipl_entry *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;
+}
+
+
+//--------------------------------------------------------------------------
+// print_boot_device
+// displays the boot device
+//--------------------------------------------------------------------------
+
+static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
+
+void
+print_boot_device(type)
+ Bit16u type;
+{
+ /* NIC appears as type 0x80 */
+ if (type == 0x80 ) type = 0x4;
+ if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
+ printf("Booting from %s...\n", drivetypes[type]);
+}
+
+//--------------------------------------------------------------------------
+// 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 from %s failed", drivetypes[type]);
+ 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");
+}
+
+//--------------------------------------------------------------------------
+// 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);
+}
+
+#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
+
+// 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_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,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);
+}
+
+// ---------------------------------------------------------------------------
+// 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;
+ 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, 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
+
+ sectors = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
+
+ 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, sectors);
+ 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 /= 63;
+ heads = sectors / 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 / 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, 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);
+ sizeinmb >>= 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;
+ }
+ 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);
+ printf(" ATA-%d Hard-Disk (%lu MBytes)\n", version, sizeinmb);
+ 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;
+ 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
+ max=0xff;
+ while(--max>0) {
+ Bit8u status = inb(iobase1+ATA_CB_STAT);
+ if ((status & ATA_CB_STAT_BSY) != 0) break;
+ }
+
+// 8.2.1 (f) -- clear SRST
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+ if (read_byte(ebda_seg,&EbdaData->ata.devices[device].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) ) {
+
+// 8.2.1 (h) -- wait for not BSY
+ max=0xff;
+ while(--max>0) {
+ Bit8u status = inb(iobase1+ATA_CB_STAT);
+ if ((status & ATA_CB_STAT_BSY) == 0) break;
+ }
+ }
+ }
+
+// 8.2.1 (i) -- wait for DRDY
+ max=0xfff;
+ while(--max>0) {
+ Bit8u status = inb(iobase1+ATA_CB_STAT);
+ if ((status & ATA_CB_STAT_RDY) != 0) break;
+ }
+
+ // 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, segment, offset)
+Bit16u device, command, count, cylinder, head, sector, segment, offset;
+Bit32u lba;
+{
+ 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;
+
+ // sector will be 0 only on lba access. Convert to lba-chs
+ if (sector == 0) {
+ sector = (Bit16u) (lba & 0x000000ffL);
+ lba >>= 8;
+ cylinder = (Bit16u) (lba & 0x0000ffffL);
+ lba >>= 16;
+ head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
+ }
+
+ // 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);
+ 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);
+
+ while (1) {
+ status = inb(iobase1 + ATA_CB_STAT);
+ if ( !(status & ATA_CB_STAT_BSY) ) break;
+ }
+
+ 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--;
+ 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, segment, offset)
+Bit16u device, command, count, cylinder, head, sector, segment, offset;
+Bit32u lba;
+{
+ 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;
+
+ // sector will be 0 only on lba access. Convert to lba-chs
+ if (sector == 0) {
+ sector = (Bit16u) (lba & 0x000000ffL);
+ lba >>= 8;
+ cylinder = (Bit16u) (lba & 0x0000ffffL);
+ lba >>= 16;
+ head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
+ }
+
+ // 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);
+ 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);
+
+ while (1) {
+ status = inb(iobase1 + ATA_CB_STAT);
+ if ( !(status & ATA_CB_STAT_BSY) ) break;
+ }
+
+ 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
+ while (1) {
+ status = inb(iobase1 + ATA_CB_STAT);
+ if ( !(status & ATA_CB_STAT_BSY) ) break;
+ }
+
+ 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) {
+ status = inb(iobase1 + ATA_CB_STAT);
+ }
+ else {
+ while (1) {
+
+ status = inb(iobase1 + ATA_CB_STAT);
+
+ // Check if command completed
+ if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
+
+ if (status & ATA_CB_STAT_ERR) {
+ BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
+ return 3;
+ }
+
+ // Device must be ready to send data
+ 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_packet : not ready (status %02x)\n", status);
+ return 4;
+ }
+
+ // 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)
+ Bit16u device;
+{
+ Bit8u atacmd[12];
+ Bit8u buffer[16];
+ Bit8u i;
+
+ memsetb(get_SS(),atacmd,0,12);
+
+ // Request SENSE
+ atacmd[0]=0x03;
+ atacmd[4]=0x20;
+ if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
+ return 0x0002;
+
+ if ((buffer[0] & 0x7e) == 0x70) {
+ return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
+ }
+
+ return 0;
+}
+
+ Bit16u
+atapi_is_ready(device)
+ Bit16u device;
+{
+ Bit8u atacmd[12];
+ Bit8u buffer[];
+
+ memsetb(get_SS(),atacmd,0,12);
+
+ // Test Unit Ready
+ if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
+ return 0x000f;
+
+ if (atapi_get_sense(device) !=0 ) {
+ memsetb(get_SS(),atacmd,0,12);
+
+ // try to send Test Unit Ready again
+ if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
+ return 0x000f;
+
+ return atapi_get_sense(device);
+ }
+ 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;
+
+ // 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
+
+
+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_function.CX [bx]
+ shl eax, #16
+ SEG SS
+ mov ax, _int15_function.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;
+ }
+
+ 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, 0x0009fc00L, 0, 0, 1);
+ regs.u.r32.ebx = 1;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ break;
+ case 1:
+ set_e820_range(ES, regs.u.r16.di,
+ 0x0009fc00L, 0x000a0000L, 0, 0, 2);
+ regs.u.r32.ebx = 2;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ break;
+ case 2:
+ set_e820_range(ES, regs.u.r16.di,
+ 0x000e8000L, 0x00100000L, 0, 0, 2);
+ regs.u.r32.ebx = 3;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ break;
+ case 3:
+ set_e820_range(ES, regs.u.r16.di,
+ 0x00100000L,
+ extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
+ regs.u.r32.ebx = 4;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ 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;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ break;
+ case 5:
+ /* 3 pages before the bios, we map the vmx tss pages */
+ set_e820_range(ES, regs.u.r16.di, 0xfffbd000L,
+ 0xfffc0000L, 0, 0, 2);
+ regs.u.r32.ebx = 6;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ 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;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ 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;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ default: /* AX=E820, DX=534D4150, BX unrecognized */
+ goto int15_unimplemented;
+ break;
+ }
+ } 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;
+ 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 = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
+ 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, segment, offset);
+ else
+ status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, 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 physical geometry from table
+ 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);
+
+ // Compute sector count seen by int13
+ lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
+ CX = lba >> 16;
+ DX = lba & 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);
+
+ // Can't use 64 bits lba
+ lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
+ if (lba != 0L) {
+ BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
+ goto int13_fail;
+ }
+
+ // Get 32 bits lba and check
+ lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
+ if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
+ 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, segment, offset);
+ else
+ status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, 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 = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
+ 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, 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); // FIXME should be Bit64
+ write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);
+ 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);
+ 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, (&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 15
+ 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);
+ 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, (&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); // HD0: 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
+
+
+//////////////////////
+// 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);
+}
+
+#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 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;
+
+ struct ipl_entry 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;
+ 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.type);
+
+ switch(e.type) {
+ case 0x01: /* FDD */
+ case 0x02: /* HDD */
+
+ bootdrv = (e.type == 0x02) ? 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 != 0x01) || !((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 0x03: /* 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);
+ /* Canonicalize bootseg:bootip */
+ bootip = (bootseg & 0x0fff) << 4;
+ bootseg &= 0xf000;
+ break;
+#endif
+
+ case 0x80: /* 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
+ ;; 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:
+ call eoi_both_pics
+
+ xor ax, ax
+ mov ds, ax
+
+ jmp far ptr [0x467]
+
+
+;--------------------
+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 0x7000 ;; 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 0x07 ;; 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 eax, dx
+ cmp eax, [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
+ inc dx
+ inc dx
+ 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
+
+ ;; copy rombios32 code to ram (ram offset = 1MB)
+ mov esi, #0xfffe0000
+ mov edi, #0x00040000
+ mov ecx, #0x10000 / 4
+ rep
+ movsd
+
+ ;; init the stack pointer
+ mov esp, #0x00080000
+
+ ;; call rombios32 code
+ mov eax, #0x00040000
+ call eax
+
+ ;; reset the memory (some boot loaders such as syslinux suppose
+ ;; that the memory is set to zero)
+ mov edi, #0x00040000
+ mov ecx, #0x40000 / 4
+ xor eax, eax
+ rep
+ stosd
+
+ ;; reset the memory (some boot loaders such as syslinux suppose
+ ;; that the memory is set to zero)
+ mov edi, #0x00040000
+ mov ecx, #0x40000 / 4
+ xor eax, eax
+ rep
+ stosd
+
+ ;; 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
+
+
+; 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)
+
+ mov cx, #0xc000
+rom_scan_loop:
+ 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:
+
+ xor bx, bx ;; Restore DS back to 0000:
+ mov ds, bx
+ push ax ;; Save AX
+ push di ;; Save DI
+ ;; Push addr of ROM entry point
+ push cx ;; Push seg
+ push #0x0003 ;; Push offset
+
+ ;; 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 ax, #0xf000
+ mov es, ax
+ 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, 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.
+ 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], #0x80 ;; This entry is a BEV device
+ mov 6[bx], cx ;; Build a far pointer from the segment...
+ mov 4[bx], ax ;; and the offset
+ 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
+ cmp cx, #0xe000
+ jbe rom_scan_loop
+
+ xor ax, ax ;; Restore DS back to 0000:
+ mov ds, ax
+ ret
+
+;; for 'C' strings and other data, insert them here with
+;; a the following hack:
+;; DATA_SEG_DEFS_HERE
+
+
+;; 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
+bios_table_area_end:
+post:
+
+ 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
+
+ ;; Examine CMOS shutdown status.
+ ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = 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
+
+ ;; 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
+ inc bx
+ inc bx
+ mov [bx], dx
+ inc bx
+ inc bx
+ 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
+ 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
+
+#if BX_ROMBIOS32
+ call rombios32_init
+#else
+ call pcibios_init_iomem_bases
+ call pcibios_init_irqs
+#endif
+
+ call _init_boot_vectors
+
+ call rom_scan
+
+ call _print_bios_banner
+
+ ;;
+ ;; Floppy setup
+ ;;
+ call floppy_drive_post
+
+#if BX_USE_ATADRV
+
+ ;;
+ ;; Hard Drive setup
+ ;;
+ call hard_drive_post
+
+ ;;
+ ;; ATA/ATAPI driver setup
+ ;;
+ call _ata_init
+ call _ata_detect
+ ;;
+#else // BX_USE_ATADRV
+
+ ;;
+ ;; Hard Drive setup
+ ;;
+ call hard_drive_post
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+ ;;
+ ;; eltorito floppy/harddisk emulation from cd
+ ;;
+ call _cdemu_init
+ ;;
+#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
+// 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..9f597a36e
--- /dev/null
+++ b/kvm/bios/rombios.h
@@ -0,0 +1,59 @@
+/////////////////////////////////////////////////////////////////////////
+// $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 CPU_COUNT_ADDR 0xf000
diff --git a/kvm/bios/rombios32.c b/kvm/bios/rombios32.c
new file mode 100755
index 000000000..314df94ef
--- /dev/null
+++ b/kvm/bios/rombios32.c
@@ -0,0 +1,1384 @@
+/////////////////////////////////////////////////////////////////////////
+// $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_APIC (1 << 9)
+
+#define APIC_BASE ((uint8_t *)0xfee00000)
+#define APIC_ICR_LOW 0x300
+#define APIC_SVR 0x0F0
+#define APIC_ID 0x020
+#define APIC_LVT3 0x370
+
+#define APIC_ENABLED 0x0100
+
+#define AP_BOOT_ADDR 0x10000
+
+#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 */
+
+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 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;
+}
+
+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;
+}
+
+void bios_printf(int flags, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ const char *s;
+
+ 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
+ }
+}
+
+int smp_cpus;
+uint32_t cpuid_features;
+uint32_t cpuid_ext_features;
+unsigned long ram_size;
+#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 cpu_probe(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ cpuid(1, eax, ebx, ecx, edx);
+ cpuid_features = edx;
+ cpuid_ext_features = ecx;
+}
+
+static int cmos_readb(int addr)
+{
+ outb(0x70, addr);
+ return inb(0x71);
+}
+
+void ram_probe(void)
+{
+ ram_size = (cmos_readb(0x34) | (cmos_readb(0x35) << 8)) * 65536 +
+ 16 * 1024 * 1024;
+#ifdef BX_USE_EBDA_TABLES
+ ebda_cur_addr = ((*(uint16_t *)(0x40e)) << 4) + 0x380;
+#endif
+ BX_INFO("ram_size=0x%08lx\n");
+}
+
+/****************************************************/
+/* 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;
+
+ 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);
+
+ writew((void *)CPU_COUNT_ADDR, 1);
+ /* 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((void *)CPU_COUNT_ADDR))
+ ;
+#endif
+
+ smp_cpus = readw((void *)CPU_COUNT_ADDR);
+ }
+ BX_INFO("Found %d cpu(s)\n", 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 */
+
+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 int 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 0;
+ }
+ }
+ return -1;
+}
+
+static void bios_shadow_init(PCIDevice *d)
+{
+ int v;
+
+ if (find_bios_table_area() < 0)
+ return;
+
+ /* remap the BIOS to shadow RAM an keep it read/write while we
+ are writing tables */
+ memcpy((void *)BIOS_TMP_STORAGE, (void *)0x000f0000, 0x10000);
+ v = pci_config_readb(d, 0x59);
+ v = (v & 0x0f) | (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 == 0x8086 && device_id == 0x7000) {
+ int i, irq;
+ uint8_t elcr[2];
+
+ /* PIIX3 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 init: elcr=%02x %02x\n",
+ elcr[0], elcr[1]);
+ } else if (vendor_id == 0x8086 && device_id == 0x1237) {
+ /* 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)
+{
+ /* 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, pci_config_readl(d, 0x58) | (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);
+
+ /* enable the SMM memory window */
+ pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x48);
+
+ /* 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 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\n",
+ d->bus, d->devfn, vendor_id, device_id);
+ switch(class) {
+ case 0x0101:
+ if (vendor_id == 0x8086 && device_id == 0x7010) {
+ /* PIIX3 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:
+ 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:
+ /* PIC */
+ if (vendor_id == 0x1014) {
+ /* 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 == 0x0106b &&
+ (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;
+ }
+ }
+ 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 == 0x8086 && device_id == 0x7113) {
+ /* PIIX4 Power Management device (for ACPI) */
+ pm_io_base = PM_IO_BASE;
+ pci_config_writel(d, 0x40, pm_io_base | 1);
+ pci_config_writeb(d, 0x80, 0x01); /* enable PM io space */
+ smb_io_base = SMB_IO_BASE;
+ pci_config_writel(d, 0x90, smb_io_base | 1);
+ pci_config_writeb(d, 0xd2, 0x09); /* enable SMBus io space */
+ pm_sci_int = pci_config_readb(d, PCI_INTERRUPT_LINE);
+#ifdef BX_USE_SMM
+ smm_init(d);
+#endif
+ 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_QEMU
+ if (smp_cpus <= 1)
+ return;
+#endif
+
+#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 */
+ putle16(&q, smp_cpus + 18); /* entry count */
+ 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 < smp_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
+ putb(&q, 1); /* cpu flags: enabled */
+ 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++) {
+ 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 */
+ 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) */
+
+#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
+};
+
+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 */
+};
+
+/*
+ * 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 [2]; /* Array of pointers to other */
+ /* ACPI tables */
+};
+
+/*
+ * 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 */
+};
+
+
+/*
+ * 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
+};
+
+/*
+ * 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
+};
+
+
+/* Values for Type in APIC_HEADER_DEF */
+
+#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 */
+
+/*
+ * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE)
+ */
+#define APIC_HEADER_DEF /* Common APIC sub-structure header */\
+ uint8_t type; \
+ uint8_t length;
+
+/* Sub-structures for MADT */
+
+struct madt_processor_apic
+{
+ APIC_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
+};
+
+struct madt_io_apic
+{
+ APIC_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 */
+};
+
+#include "acpi-dsdt.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)
+{
+ memcpy(h->signature, sig, 4);
+ h->length = cpu_to_le32(len);
+ h->revision = 1;
+#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);
+}
+
+/* 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;
+ uint32_t base_addr, rsdt_addr, fadt_addr, addr, facs_addr, dsdt_addr;
+ uint32_t acpi_tables_size, madt_addr, madt_size;
+ int i;
+
+ /* 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
+
+ addr = base_addr = ram_size - ACPI_DATA_SIZE;
+ rsdt_addr = addr;
+ rsdt = (void *)(addr);
+ addr += sizeof(*rsdt);
+
+ 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(AmlCode);
+
+ addr = (addr + 7) & ~7;
+ madt_addr = addr;
+ madt_size = sizeof(*madt) +
+ sizeof(struct madt_processor_apic) * smp_cpus +
+ sizeof(struct madt_io_apic);
+ madt = (void *)(addr);
+ addr += madt_size;
+
+ 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);
+
+ /* 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);
+
+ /* RSDT */
+ rsdt->table_offset_entry[0] = cpu_to_le32(fadt_addr);
+ rsdt->table_offset_entry[1] = cpu_to_le32(madt_addr);
+ acpi_build_table_header((struct acpi_table_header *)rsdt,
+ "RSDT", sizeof(*rsdt));
+
+ /* 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(0x0fff); // C2 state not supported
+ fadt->plvl3_lat = cpu_to_le16(0x0fff); // C3 state not supported
+ /* WBINVD + PROC_C1 + PWR_BUTTON + SLP_BUTTON + FIX_RTC */
+ fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 4) | (1 << 5) | (1 << 6));
+ acpi_build_table_header((struct acpi_table_header *)fadt, "FACP",
+ sizeof(*fadt));
+
+ /* FACS */
+ memset(facs, 0, sizeof(*facs));
+ memcpy(facs->signature, "FACS", 4);
+ facs->length = cpu_to_le32(sizeof(*facs));
+
+ /* DSDT */
+ memcpy(dsdt, AmlCode, sizeof(AmlCode));
+
+ /* MADT */
+ {
+ struct madt_processor_apic *apic;
+ struct madt_io_apic *io_apic;
+
+ memset(madt, 0, madt_size);
+ madt->local_apic_address = cpu_to_le32(0xfee00000);
+ madt->flags = cpu_to_le32(1);
+ apic = (void *)(madt + 1);
+ for(i=0;i<smp_cpus;i++) {
+ apic->type = APIC_PROCESSOR;
+ apic->length = sizeof(*apic);
+ apic->processor_id = i;
+ apic->local_apic_id = i;
+ apic->flags = cpu_to_le32(1);
+ 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);
+
+ acpi_build_table_header((struct acpi_table_header *)madt,
+ "APIC", madt_size);
+ }
+}
+
+void rombios32_init(void)
+{
+ BX_INFO("Starting rombios32\n");
+
+ ram_probe();
+
+ cpu_probe();
+
+ smp_probe();
+
+ pci_bios_init();
+
+ if (bios_table_cur_addr != 0) {
+
+ mptable_init();
+
+ if (acpi_enabled)
+ acpi_bios_init();
+
+ bios_lock_shadow_ram();
+ }
+}
diff --git a/kvm/bios/rombios32.ld b/kvm/bios/rombios32.ld
new file mode 100644
index 000000000..38a1f1312
--- /dev/null
+++ b/kvm/bios/rombios32.ld
@@ -0,0 +1,19 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start);
+SECTIONS
+{
+ . = 0x00040000;
+ .text : { *(.text) }
+ .rodata : { *(.rodata) }
+ . = ALIGN(4096);
+ .data : { *(.data) }
+ __bss_start = . ;
+ .bss : { *(.bss) *(COMMON) }
+ _end = . ;
+ /DISCARD/ : { *(.stab)
+ *(.stabstr)
+ *(.comment)
+ *(.note)
+ }
+}
diff --git a/kvm/bios/rombios32start.S b/kvm/bios/rombios32start.S
new file mode 100644
index 000000000..0fe2eabd5
--- /dev/null
+++ b/kvm/bios/rombios32start.S
@@ -0,0 +1,98 @@
+/////////////////////////////////////////////////////////////////////////
+// $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 $_end, %ecx
+ sub %edi, %ecx
+ rep stosb
+
+ jmp rombios32_init
+
+ .code16
+smp_ap_boot_code_start:
+ xor %ax, %ax
+ mov %ax, %ds
+ incw CPU_COUNT_ADDR
+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..409670dfe
--- /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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/configure b/kvm/configure
new file mode 100755
index 000000000..dadda8e0a
--- /dev/null
+++ b/kvm/configure
@@ -0,0 +1,136 @@
+#!/bin/bash
+
+prefix=/usr/local
+kerneldir=/lib/modules/$(uname -r)/build
+want_module=1
+qemu_cc=
+qemu_cflags=
+qemu_ldflags=
+enable_alsa=
+disable_vnc_tls=
+disable_gcc_check=
+cross_prefix=
+arch=`uname -m`
+target_exec=
+
+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
+ --kerneldir=DIR kernel build directory ($kerneldir)
+ --qemu-cc=CC specify compiler for qemu (must be gcc-3.x)
+ --qemu-cflags=CFLAGS CFLAGS to add to qemu configuration
+ --qemu-ldflags=LDFLAGS LDFLAGS to add to qemu configuration
+ --enable-alsa enable alsa support for qemu
+ --disable-vnc-tls disable vnc tls support for qemu
+ --disable-gcc-check don't insist on gcc-3.x
+ CAUTION: this will break running without kvm
+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"
+ ;;
+ --with-patched-kernel)
+ want_module=
+ ;;
+ --qemu-cc)
+ qemu_cc="$arg"
+ ;;
+ --qemu-cflags)
+ qemu_cflags="$arg"
+ ;;
+ --qemu-ldflags)
+ qemu_ldflags="$arg"
+ ;;
+ --enable-alsa)
+ enable_alsa=1
+ ;;
+ --disable-vnc-tls)
+ disable_vnc_tls=1
+ ;;
+ --disable-gcc-check)
+ disable_gcc_check=1
+ ;;
+ --arch)
+ arch="$arg"
+ ;;
+ --cross-prefix)
+ cross_prefix="$arg"
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+
+#set kenel directory
+libkvm_kerneldir="$kerneldir"
+if (( want_module )); then
+ libkvm_kerneldir=$(readlink -f kernel)
+fi
+
+#if arch is an x86 arch set to i386
+if [[ $arch = i?86 ]]; then
+ arch="i386"
+fi
+
+#set parameters compiling
+if [ "$arch" = "i386" -o "$arch" = "x86_64" ]; then
+ target_exec="x86_64-softmmu"
+ qemu_cflags="$qemu_cflags -DCONFIG_X86"
+fi
+
+if [ "$arch" = "ia64" ]; then
+ target_exec="ia64-softmmu"
+fi
+
+#configure user dir
+(cd user; ./configure --prefix="$prefix" --kerneldir="$libkvm_kerneldir" \
+ --arch="$arch" \
+ ${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" \
+ --enable-kvm --kernel-path="$libkvm_kerneldir" \
+ ${enable_alsa:+"--enable-alsa"} \
+ ${disable_vnc_tls:+"--disable-vnc-tls"} \
+ ${disable_gcc_check:+"--disable-gcc-check"} \
+ --prefix="$prefix" \
+ ${qemu_cc:+"--cc=$qemu_cc"} \
+ ${cross_prefix:+"--cross-prefix=$cross_prefix"} \
+ ${cross_prefix:+"--cpu=$arch"}
+)
+
+
+cat <<EOF > config.mak
+ARCH=$arch
+PREFIX=$prefix
+KERNELDIR=$kerneldir
+WANT_MODULE=$want_module
+CROSS_COMPILE=$cross_prefix
+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/kernel/Kbuild b/kvm/kernel/Kbuild
new file mode 100644
index 000000000..ed02f5a7d
--- /dev/null
+++ b/kvm/kernel/Kbuild
@@ -0,0 +1,8 @@
+EXTRA_CFLAGS := -I$(src)/include -include $(src)/external-module-compat.h
+obj-m := kvm.o kvm-intel.o kvm-amd.o
+kvm-objs := kvm_main.o x86.o mmu.o x86_emulate.o anon_inodes.o irq.o i8259.o \
+ lapic.o ioapic.o preempt.o
+kvm-intel-objs := vmx.o vmx-debug.o
+kvm-amd-objs := svm.o
+
+CFLAGS_kvm_main.o = -DKVM_MAIN
diff --git a/kvm/kernel/Makefile b/kvm/kernel/Makefile
new file mode 100644
index 000000000..d409a0af4
--- /dev/null
+++ b/kvm/kernel/Makefile
@@ -0,0 +1,82 @@
+include ../config.mak
+
+KVERREL = $(patsubst /lib/modules/%/build,%,$(KERNELDIR))
+
+DESTDIR=
+
+INSTALLDIR = $(patsubst %/build,%/extra,$(KERNELDIR))
+ORIGMODDIR = $(patsubst %/build,%/kernel,$(KERNELDIR))
+
+rpmrelease = devel
+
+LINUX = ../linux-2.6
+
+version = $(shell cd $(LINUX); git describe)
+
+_hack = mv $1 $1.orig && \
+ awk -v version=$(version) -f hack-module.awk $1.orig \
+ | sed '/\#include/! s/\blapic\b/l_apic/g' > $1 && rm $1.orig
+
+_unifdef = mv $1 $1.orig && \
+ unifdef -DCONFIG_X86 $1.orig > $1; \
+ [ $$? -le 1 ] && rm $1.orig
+
+hack = $(call _hack,tmp/$(strip $1))
+unifdef = $(call _unifdef,tmp/$(strip $1))
+
+all::
+ $(MAKE) -C $(KERNELDIR) M=`pwd` "$$@"
+
+sync:
+ rm -rf tmp
+ rsync --exclude='*.mod.c' -R \
+ "$(LINUX)"/arch/x86/kvm/./*.[ch] \
+ "$(LINUX)"/virt/kvm/./*.[ch] \
+ "$(LINUX)"/./include/linux/kvm*.h \
+ "$(LINUX)"/./include/asm-x86/kvm*.h \
+ tmp/
+ ln -sf asm-x86 include/asm
+
+ $(call unifdef, include/linux/kvm.h)
+ $(call unifdef, include/linux/kvm_para.h)
+ $(call unifdef, include/asm-x86/kvm.h)
+ $(call unifdef, include/asm-x86/kvm_para.h)
+ $(call hack, kvm_main.c)
+ $(call hack, mmu.c)
+ $(call hack, vmx.c)
+ $(call hack, svm.c)
+ $(call hack, x86.c)
+ $(call hack, irq.h)
+ for i in $$(find tmp -type f -printf '%P '); \
+ do cmp -s $$i tmp/$$i || cp tmp/$$i $$i; done
+
+install:
+ mkdir -p $(DESTDIR)/$(INSTALLDIR)
+ cp *.ko $(DESTDIR)/$(INSTALLDIR)
+ for i in $(ORIGMODDIR)/drivers/kvm/*.ko; do \
+ if [ -f "$$i" ]; then mv "$$i" "$$i.orig"; fi; \
+ done
+ /sbin/depmod -a
+
+tmpspec = .tmp.kvm-kmod.spec
+
+rpm-topdir := $$(pwd)/../rpmtop
+
+RPMDIR = $(rpm-topdir)/RPMS
+
+rpm: all
+ mkdir -p $(rpm-topdir)/BUILD $(RPMDIR)/$$(uname -i)
+ sed 's/^Release:.*/Release: $(rpmrelease)/; s/^%define kverrel.*/%define kverrel $(KVERREL)/' \
+ kvm-kmod.spec > $(tmpspec)
+ rpmbuild --define="kverrel $(KVERREL)" \
+ --define="objdir $$(pwd)" \
+ --define="_rpmdir $(RPMDIR)" \
+ --define="_topdir $(rpm-topdir)" \
+ -bb $(tmpspec)
+
+clean:
+ $(MAKE) -C $(KERNELDIR) M=`pwd` $@
+
+svnclean:
+ svn st | grep '^\?' | awk '{print $2}' | xargs rm -rf
+
diff --git a/kvm/kernel/anon_inodes.c b/kvm/kernel/anon_inodes.c
new file mode 100644
index 000000000..05ec84508
--- /dev/null
+++ b/kvm/kernel/anon_inodes.c
@@ -0,0 +1,221 @@
+/*
+ * fs/anon_inodes.c
+ *
+ * Copyright (C) 2007 Davide Libenzi <davidel@xmailserver.org>
+ *
+ * Thanks to Arnd Bergmann for code review and suggestions.
+ * More changes for Thomas Gleixner suggestions.
+ *
+ */
+
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/magic.h>
+#include <linux/anon_inodes.h>
+
+#include <asm/uaccess.h>
+
+static struct vfsmount *anon_inode_mnt __read_mostly;
+static struct inode *anon_inode_inode;
+static struct file_operations anon_inode_fops;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)
+
+static int anon_inodefs_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data,
+ struct vfsmount *mnt)
+{
+ return get_sb_pseudo(fs_type, "kvm_anon_inode:", NULL, 0x99700426, mnt);
+}
+
+#else
+
+static struct super_block *anon_inodefs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data)
+{
+ return get_sb_pseudo(fs_type, "kvm_anon_inode:", NULL, 0x99700426);
+}
+
+#endif
+
+static int anon_inodefs_delete_dentry(struct dentry *dentry)
+{
+ /*
+ * We faked vfs to believe the dentry was hashed when we created it.
+ * Now we restore the flag so that dput() will work correctly.
+ */
+ dentry->d_flags |= DCACHE_UNHASHED;
+ return 1;
+}
+
+static struct file_system_type anon_inode_fs_type = {
+ .name = "kvm_anon_inodefs",
+ .get_sb = anon_inodefs_get_sb,
+ .kill_sb = kill_anon_super,
+};
+static struct dentry_operations anon_inodefs_dentry_operations = {
+ .d_delete = anon_inodefs_delete_dentry,
+};
+
+/**
+ * anon_inode_getfd - creates a new file instance by hooking it up to and
+ * anonymous inode, and a dentry that describe the "class"
+ * of the file
+ *
+ * @pfd: [out] pointer to the file descriptor
+ * @dpinode: [out] pointer to the inode
+ * @pfile: [out] pointer to the file struct
+ * @name: [in] name of the "class" of the new file
+ * @fops [in] file operations for the new file
+ * @priv [in] private data for the new file (will be file's private_data)
+ *
+ * Creates a new file by hooking it on a single inode. This is useful for files
+ * that do not need to have a full-fledged inode in order to operate correctly.
+ * All the files created with anon_inode_getfd() will share a single inode, by
+ * hence saving memory and avoiding code duplication for the file/inode/dentry
+ * setup.
+ */
+int anon_inode_getfd(int *pfd, struct inode **pinode, struct file **pfile,
+ const char *name, const struct file_operations *fops,
+ void *priv)
+{
+ struct qstr this;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct file *file;
+ int error, fd;
+
+ if (IS_ERR(anon_inode_inode))
+ return -ENODEV;
+ file = get_empty_filp();
+ if (!file)
+ return -ENFILE;
+
+ inode = igrab(anon_inode_inode);
+ if (IS_ERR(inode)) {
+ error = PTR_ERR(inode);
+ goto err_put_filp;
+ }
+
+ error = get_unused_fd();
+ if (error < 0)
+ goto err_iput;
+ fd = error;
+
+ /*
+ * Link the inode to a directory entry by creating a unique name
+ * using the inode sequence number.
+ */
+ error = -ENOMEM;
+ this.name = name;
+ this.len = strlen(name);
+ this.hash = 0;
+ dentry = d_alloc(anon_inode_mnt->mnt_sb->s_root, &this);
+ if (!dentry)
+ goto err_put_unused_fd;
+ dentry->d_op = &anon_inodefs_dentry_operations;
+ /* Do not publish this dentry inside the global dentry hash table */
+ dentry->d_flags &= ~DCACHE_UNHASHED;
+ d_instantiate(dentry, inode);
+
+ file->f_vfsmnt = mntget(anon_inode_mnt);
+ file->f_dentry = dentry;
+ file->f_mapping = inode->i_mapping;
+
+ file->f_pos = 0;
+ file->f_flags = O_RDWR;
+ file->f_op = (struct file_operations *)fops;
+ file->f_mode = FMODE_READ | FMODE_WRITE;
+ file->f_version = 0;
+ file->private_data = priv;
+
+ fd_install(fd, file);
+
+ *pfd = fd;
+ *pinode = inode;
+ *pfile = file;
+ return 0;
+
+err_put_unused_fd:
+ put_unused_fd(fd);
+err_iput:
+ iput(inode);
+err_put_filp:
+ fput(file);
+ return error;
+}
+
+/*
+ * A single inode exist for all anon_inode files. Contrary to pipes,
+ * anon_inode inodes has no per-instance data associated, so we can avoid
+ * the allocation of multiple of them.
+ */
+static struct inode *anon_inode_mkinode(void)
+{
+ struct inode *inode = new_inode(anon_inode_mnt->mnt_sb);
+
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ inode->i_fop = &anon_inode_fops;
+
+ /*
+ * Mark the inode dirty from the very beginning,
+ * that way it will never be moved to the dirty
+ * list because mark_inode_dirty() will think
+ * that it already _is_ on the dirty list.
+ */
+ inode->i_state = I_DIRTY;
+ inode->i_mode = S_IRUSR | S_IWUSR;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ return inode;
+}
+
+static int anon_inode_init(void)
+{
+ int error;
+
+ error = register_filesystem(&anon_inode_fs_type);
+ if (error)
+ goto err_exit;
+ anon_inode_mnt = kern_mount(&anon_inode_fs_type);
+ if (IS_ERR(anon_inode_mnt)) {
+ error = PTR_ERR(anon_inode_mnt);
+ goto err_unregister_filesystem;
+ }
+ anon_inode_inode = anon_inode_mkinode();
+ if (IS_ERR(anon_inode_inode)) {
+ error = PTR_ERR(anon_inode_inode);
+ goto err_mntput;
+ }
+
+ return 0;
+
+err_mntput:
+ mntput(anon_inode_mnt);
+err_unregister_filesystem:
+ unregister_filesystem(&anon_inode_fs_type);
+err_exit:
+ return -ENOMEM;
+}
+
+int kvm_init_anon_inodes(void)
+{
+ return anon_inode_init();
+}
+
+void kvm_exit_anon_inodes(void)
+{
+ iput(anon_inode_inode);
+ mntput(anon_inode_mnt);
+ unregister_filesystem(&anon_inode_fs_type);
+}
diff --git a/kvm/kernel/debug.h b/kvm/kernel/debug.h
new file mode 100644
index 000000000..35793652a
--- /dev/null
+++ b/kvm/kernel/debug.h
@@ -0,0 +1,23 @@
+#ifndef __KVM_DEBUG_H
+#define __KVM_DEBUG_H
+
+#ifdef KVM_DEBUG
+
+void show_msrs(struct kvm_vcpu *vcpu);
+
+
+void show_irq(struct kvm_vcpu *vcpu, int irq);
+void show_page(struct kvm_vcpu *vcpu, gva_t addr);
+void show_u64(struct kvm_vcpu *vcpu, gva_t addr);
+void show_code(struct kvm_vcpu *vcpu);
+int vm_entry_test(struct kvm_vcpu *vcpu);
+
+void vmcs_dump(struct kvm_vcpu *vcpu);
+void regs_dump(struct kvm_vcpu *vcpu);
+void sregs_dump(struct kvm_vcpu *vcpu);
+void show_pending_interrupts(struct kvm_vcpu *vcpu);
+void vcpu_dump(struct kvm_vcpu *vcpu);
+
+#endif
+
+#endif
diff --git a/kvm/kernel/external-module-compat.h b/kvm/kernel/external-module-compat.h
new file mode 100644
index 000000000..1dc5a9677
--- /dev/null
+++ b/kvm/kernel/external-module-compat.h
@@ -0,0 +1,654 @@
+
+/*
+ * Compatibility header for building as an external module.
+ */
+
+/*
+ * Avoid picking up the kernel's kvm.h in case we have a newer one.
+ */
+
+#include <linux/compiler.h>
+#include <linux/version.h>
+#include "include/linux/kvm.h"
+#include "include/linux/kvm_para.h"
+#include <linux/cpu.h>
+#include <asm/processor.h>
+#include <linux/hrtimer.h>
+#include <asm/bitops.h>
+
+/*
+ * 2.6.16 does not have GFP_NOWAIT
+ */
+
+#include <linux/gfp.h>
+
+#ifndef GFP_NOWAIT
+#define GFP_NOWAIT (GFP_ATOMIC & ~__GFP_HIGH)
+#endif
+
+
+/*
+ * kvm profiling support needs 2.6.20
+ */
+#include <linux/profile.h>
+
+#ifndef KVM_PROFILING
+#define KVM_PROFILING 1234
+#define prof_on 4321
+#endif
+
+/*
+ * smp_call_function_single() is not exported below 2.6.20
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+
+struct scfs_thunk_info {
+ int cpu;
+ void (*func)(void *info);
+ void *info;
+};
+
+static inline void scfs_thunk(void *_thunk)
+{
+ struct scfs_thunk_info *thunk = _thunk;
+
+ if (raw_smp_processor_id() == thunk->cpu)
+ thunk->func(thunk->info);
+}
+
+static inline int smp_call_function_single1(int cpu, void (*func)(void *info),
+ void *info, int nonatomic, int wait)
+{
+ int r, this_cpu;
+ struct scfs_thunk_info thunk;
+
+ this_cpu = get_cpu();
+ if (cpu == this_cpu) {
+ r = 0;
+ local_irq_disable();
+ func(info);
+ local_irq_enable();
+ } else {
+ thunk.cpu = cpu;
+ thunk.func = func;
+ thunk.info = info;
+ r = smp_call_function(scfs_thunk, &thunk, 0, 1);
+ }
+ put_cpu();
+ return r;
+}
+
+#define smp_call_function_single smp_call_function_single1
+
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+/*
+ * pre 2.6.23 doesn't handle smp_call_function_single on current cpu
+ */
+
+#include <linux/smp.h>
+
+static inline int smp_call_function_single2(int cpu, void (*func)(void *info),
+ void *info, int nonatomic, int wait)
+{
+ int this_cpu, r;
+
+ this_cpu = get_cpu();
+ if (cpu == this_cpu) {
+ r = 0;
+ local_irq_disable();
+ func(info);
+ local_irq_enable();
+ } else
+ r = smp_call_function_single(cpu, func, info, nonatomic, wait);
+ put_cpu();
+ return r;
+}
+
+#define smp_call_function_single smp_call_function_single2
+
+#endif
+
+/*
+ * The cpu hotplug stubs are broken if !CONFIG_CPU_HOTPLUG
+ */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15)
+#define DEFINE_MUTEX(a) DECLARE_MUTEX(a)
+#define mutex_lock_interruptible(a) down_interruptible(a)
+#define mutex_unlock(a) up(a)
+#define mutex_lock(a) down(a)
+#define mutex_init(a) init_MUTEX(a)
+#define mutex_trylock(a) down_trylock(a)
+#define mutex semaphore
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+#ifndef kzalloc
+#define kzalloc(size,flags) \
+({ \
+ void *__ret = kmalloc(size, flags); \
+ if (__ret) \
+ memset(__ret, 0, size); \
+ __ret; \
+})
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
+#ifndef kmem_cache_zalloc
+#define kmem_cache_zalloc(cache,flags) \
+({ \
+ void *__ret = kmem_cache_alloc(cache, flags); \
+ if (__ret) \
+ memset(__ret, 0, kmem_cache_size(cache)); \
+ __ret; \
+})
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+
+#ifndef CONFIG_HOTPLUG_CPU
+#define register_cpu_notifier(nb) (0)
+#endif
+
+#endif
+
+#include <linux/miscdevice.h>
+#ifndef KVM_MINOR
+#define KVM_MINOR 232
+#endif
+
+#include <linux/notifier.h>
+#ifndef CPU_TASKS_FROZEN
+
+#define CPU_TASKS_FROZEN 0x0010
+#define CPU_ONLINE_FROZEN (CPU_ONLINE | CPU_TASKS_FROZEN)
+#define CPU_UP_PREPARE_FROZEN (CPU_UP_PREPARE | CPU_TASKS_FROZEN)
+#define CPU_UP_CANCELED_FROZEN (CPU_UP_CANCELED | CPU_TASKS_FROZEN)
+#define CPU_DOWN_PREPARE_FROZEN (CPU_DOWN_PREPARE | CPU_TASKS_FROZEN)
+#define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN)
+#define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN)
+
+#endif
+
+#ifndef CPU_DYING
+#define CPU_DYING 0x000A
+#define CPU_DYING_FROZEN (CPU_DYING | CPU_TASKS_FROZEN)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+
+#ifndef _EFER_SCE
+#define _EFER_SCE 0 /* SYSCALL/SYSRET */
+#endif
+
+#ifndef EFER_SCE
+#define EFER_SCE (1<<_EFER_SCE)
+#endif
+
+#endif
+
+/*
+ * For set_64bit(), which is in a new file asm/cmpxchg.h in newer kernels.
+ */
+#include <asm/system.h>
+
+#define anon_inode_getfd kvm_anon_inode_getfd
+int kvm_init_anon_inodes(void);
+void kvm_exit_anon_inodes(void);
+
+#include <linux/smp.h>
+
+#define on_cpu kvm_on_cpu
+
+/*
+ * Call a function on one processor, which might be the currently executing
+ * processor.
+ */
+static inline int on_cpu(int cpu, void (*func) (void *info), void *info,
+ int retry, int wait)
+{
+ int ret;
+ int this_cpu;
+
+ this_cpu = get_cpu();
+ if (this_cpu == cpu) {
+ local_irq_disable();
+ func(info);
+ local_irq_enable();
+ ret = 0;
+ } else
+ ret = smp_call_function_single(cpu, func, info, retry, wait);
+ put_cpu();
+ return ret;
+}
+
+#ifndef X86_CR0_PE
+#define X86_CR0_PE 0x00000001
+#endif
+
+#ifndef X86_CR0_MP
+#define X86_CR0_MP 0x00000002
+#endif
+
+#ifndef X86_CR0_EM
+#define X86_CR0_EM 0x00000004
+#endif
+
+#ifndef X86_CR0_TS
+#define X86_CR0_TS 0x00000008
+#endif
+
+#ifndef X86_CR0_ET
+#define X86_CR0_ET 0x00000010
+#endif
+
+#ifndef X86_CR0_NE
+#define X86_CR0_NE 0x00000020
+#endif
+
+#ifndef X86_CR0_WP
+#define X86_CR0_WP 0x00010000
+#endif
+
+#ifndef X86_CR0_AM
+#define X86_CR0_AM 0x00040000
+#endif
+
+#ifndef X86_CR0_NW
+#define X86_CR0_NW 0x20000000
+#endif
+
+#ifndef X86_CR0_CD
+#define X86_CR0_CD 0x40000000
+#endif
+
+#ifndef X86_CR0_PG
+#define X86_CR0_PG 0x80000000
+#endif
+
+#ifndef X86_CR3_PWT
+#define X86_CR3_PWT 0x00000008
+#endif
+
+#ifndef X86_CR3_PCD
+#define X86_CR3_PCD 0x00000010
+#endif
+
+#ifndef X86_CR4_VMXE
+#define X86_CR4_VMXE 0x00002000
+#endif
+
+#undef X86_CR8_TPR
+#define X86_CR8_TPR 0x0f
+
+/*
+ * 2.6.23 removed the cache destructor
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+# define kmem_cache_create(name, size, align, flags, ctor) \
+ kmem_cache_create(name, size, align, flags, ctor, NULL)
+#endif
+
+/*
+ * 2.6.22 does not define set_64bit() under nonpae
+ */
+#ifdef CONFIG_X86_32
+
+#include <asm/cmpxchg.h>
+
+static inline void __kvm_set_64bit(u64 *ptr, u64 val)
+{
+ unsigned int low = val;
+ unsigned int high = val >> 32;
+
+ __asm__ __volatile__ (
+ "\n1:\t"
+ "movl (%0), %%eax\n\t"
+ "movl 4(%0), %%edx\n\t"
+ "lock cmpxchg8b (%0)\n\t"
+ "jnz 1b"
+ : /* no outputs */
+ : "D"(ptr),
+ "b"(low),
+ "c"(high)
+ : "ax","dx","memory");
+}
+
+#undef set_64bit
+#define set_64bit __kvm_set_64bit
+
+static inline unsigned long long __kvm_cmpxchg64(volatile void *ptr,
+ unsigned long long old,
+ unsigned long long new)
+{
+ unsigned long long prev;
+ __asm__ __volatile__(LOCK_PREFIX "cmpxchg8b %3"
+ : "=A"(prev)
+ : "b"((unsigned long)new),
+ "c"((unsigned long)(new >> 32)),
+ "m"(*__xg(ptr)),
+ "0"(old)
+ : "memory");
+ return prev;
+}
+
+#define kvm_cmpxchg64(ptr,o,n)\
+ ((__typeof__(*(ptr)))__kvm_cmpxchg64((ptr),(unsigned long long)(o),\
+ (unsigned long long)(n)))
+
+#undef cmpxchg64
+#define cmpxchg64(ptr, o, n) kvm_cmpxchg64(ptr, o, n)
+
+#endif
+
+#ifndef CONFIG_PREEMPT_NOTIFIERS
+
+struct preempt_notifier;
+
+struct preempt_ops {
+ void (*sched_in)(struct preempt_notifier *notifier, int cpu);
+ void (*sched_out)(struct preempt_notifier *notifier,
+ struct task_struct *next);
+};
+
+struct preempt_notifier {
+ struct list_head link;
+ struct task_struct *tsk;
+ struct preempt_ops *ops;
+};
+
+void preempt_notifier_register(struct preempt_notifier *notifier);
+void preempt_notifier_unregister(struct preempt_notifier *notifier);
+
+static inline void preempt_notifier_init(struct preempt_notifier *notifier,
+ struct preempt_ops *ops)
+{
+ notifier->ops = ops;
+}
+
+void start_special_insn(void);
+void end_special_insn(void);
+void in_special_section(void);
+void special_reload_dr7(void);
+
+void preempt_notifier_sys_init(void);
+void preempt_notifier_sys_exit(void);
+
+#else
+
+static inline void start_special_insn(void) {}
+static inline void end_special_insn(void) {}
+static inline void in_special_section(void) {}
+static inline void special_reload_dr7(void) {}
+
+static inline void preempt_notifier_sys_init(void) {}
+static inline void preempt_notifier_sys_exit(void) {}
+
+#endif
+
+/* HRTIMER_MODE_ABS started life with a different name */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define HRTIMER_MODE_ABS HRTIMER_ABS
+#endif
+
+/* div64_64 is fairly new */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21)
+
+#ifdef CONFIG_64BIT
+
+static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor)
+{
+ return dividend / divisor;
+}
+
+#else
+
+/* 64bit divisor, dividend and result. dynamic precision */
+static inline uint64_t div64_64(uint64_t dividend, uint64_t divisor)
+{
+ uint32_t high, d;
+
+ high = divisor >> 32;
+ if (high) {
+ unsigned int shift = fls(high);
+
+ d = divisor >> shift;
+ dividend >>= shift;
+ } else
+ d = divisor;
+
+ do_div(dividend, d);
+
+ return dividend;
+}
+
+#endif
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+
+typedef _Bool bool;
+
+#endif
+
+/*
+ * PF_VCPU is a Linux 2.6.24 addition
+ */
+
+#include <linux/sched.h>
+
+#ifndef PF_VCPU
+#define PF_VCPU 0
+#endif
+
+/*
+ * smp_call_function_mask() is not defined/exported below 2.6.24
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+
+#include <linux/smp.h>
+
+struct kvm_call_data_struct {
+ void (*func) (void *info);
+ void *info;
+ atomic_t started;
+ atomic_t finished;
+ int wait;
+};
+
+static void kvm_ack_smp_call(void *_data)
+{
+ struct kvm_call_data_struct *data = _data;
+ /* if wait == 0, data can be out of scope
+ * after atomic_inc(info->started)
+ */
+ void (*func) (void *info) = data->func;
+ void *info = data->info;
+ int wait = data->wait;
+
+ smp_mb();
+ atomic_inc(&data->started);
+ (*func)(info);
+ if (wait) {
+ smp_mb();
+ atomic_inc(&data->finished);
+ }
+}
+
+static inline int kvm_smp_call_function_mask(cpumask_t mask,
+ void (*func) (void *info), void *info, int wait)
+{
+ struct kvm_call_data_struct data;
+ cpumask_t allbutself;
+ int cpus;
+ int cpu;
+ int me;
+
+ me = get_cpu();
+ allbutself = cpu_online_map;
+ cpu_clear(me, allbutself);
+
+ cpus_and(mask, mask, allbutself);
+ cpus = cpus_weight(mask);
+
+ if (!cpus)
+ goto out;
+
+ data.func = func;
+ data.info = info;
+ atomic_set(&data.started, 0);
+ data.wait = wait;
+ if (wait)
+ atomic_set(&data.finished, 0);
+
+ for (cpu = first_cpu(mask); cpu != NR_CPUS; cpu = next_cpu(cpu, mask))
+ smp_call_function_single(cpu, kvm_ack_smp_call, &data, 1, 0);
+
+ while (atomic_read(&data.started) != cpus) {
+ cpu_relax();
+ barrier();
+ }
+
+ if (!wait)
+ goto out;
+
+ while (atomic_read(&data.finished) != cpus) {
+ cpu_relax();
+ barrier();
+ }
+out:
+ put_cpu();
+ return 0;
+}
+
+#define smp_call_function_mask kvm_smp_call_function_mask
+
+#endif
+
+/* CONFIG_HAS_IOMEM is apparently fairly new too (2.6.21 for x86_64). */
+#ifndef CONFIG_HAS_IOMEM
+#define CONFIG_HAS_IOMEM 1
+#endif
+
+/* Some kernels don't make dest_* available with some configs. */
+
+#include <asm/io_apic.h>
+
+#ifndef dest_Fixed
+#define dest_Fixed 0
+#define dest_LowestPrio 1
+#define dest_SMI 2
+#define dest__reserved_1 3
+#define dest_NMI 4
+#define dest_INIT 5
+#define dest__reserved_2 6
+#define dest_ExtINT 7
+#endif
+
+/* empty_zero_page isn't exported in all kernels */
+#include <asm/pgtable.h>
+
+#define empty_zero_page kvm_empty_zero_page
+
+static char empty_zero_page[PAGE_SIZE];
+
+static inline void blahblah(void)
+{
+ (void)empty_zero_page[0];
+}
+
+/* __mmdrop() is not exported before 2.6.25 */
+#include <linux/sched.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+
+#define mmdrop(x) do { (void)(x); } while (0)
+
+#endif
+
+/* X86_FEATURE_NX is missing in some x86_64 kernels */
+
+#include <asm/cpufeature.h>
+
+#ifndef X86_FEATURE_NX
+#define X86_FEATURE_NX (1*32+20)
+#endif
+
+#undef true
+#define true 1
+#undef false
+#define false 0
+
+/* EFER_LMA and EFER_LME are missing in pre 2.6.24 i386 kernels */
+#ifndef EFER_LME
+#define _EFER_LME 8 /* Long mode enable */
+#define _EFER_LMA 10 /* Long mode active (read-only) */
+#define EFER_LME (1<<_EFER_LME)
+#define EFER_LMA (1<<_EFER_LMA)
+#endif
+
+/* vm ops ->fault() was introduced in 2.6.23. */
+#include <linux/mm.h>
+
+#ifdef KVM_MAIN
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+struct vm_fault {
+ unsigned int flags;
+ pgoff_t pgoff;
+ void __user *virtual_address;
+ struct page *page;
+};
+
+static int kvm_vcpu_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+static int kvm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+
+static inline struct page *kvm_nopage_to_fault(
+ int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf),
+ struct vm_area_struct *vma,
+ unsigned long address,
+ int *type)
+{
+ struct vm_fault vmf;
+ int ret;
+
+ vmf.pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ vmf.virtual_address = (void __user *)address;
+ ret = fault(vma, &vmf);
+ if (ret)
+ return NOPAGE_SIGBUS;
+ *type = VM_FAULT_MINOR;
+ return vmf.page;
+}
+
+static inline struct page *__kvm_vcpu_fault(struct vm_area_struct *vma,
+ unsigned long address,
+ int *type)
+{
+ return kvm_nopage_to_fault(kvm_vcpu_fault, vma, address, type);
+}
+
+static inline struct page *__kvm_vm_fault(struct vm_area_struct *vma,
+ unsigned long address,
+ int *type)
+{
+ return kvm_nopage_to_fault(kvm_vm_fault, vma, address, type);
+}
+
+#define VMA_OPS_FAULT(x) nopage
+#define VMA_OPS_FAULT_FUNC(x) __##x
+
+#else
+
+#define VMA_OPS_FAULT(x) x
+#define VMA_OPS_FAULT_FUNC(x) x
+
+#endif
+#endif
diff --git a/kvm/kernel/hack-module.awk b/kvm/kernel/hack-module.awk
new file mode 100644
index 000000000..7993aa258
--- /dev/null
+++ b/kvm/kernel/hack-module.awk
@@ -0,0 +1,93 @@
+
+/^int kvm_init\(/ { anon_inodes = 1 }
+
+/return 0;/ && anon_inodes {
+ print "\tr = kvm_init_anon_inodes();";
+ print "\tif (r) {";
+ print "\t\t__free_page(bad_page);";
+ print "\t\tgoto out;";
+ print "\t}";
+ print "\tpreempt_notifier_sys_init();";
+ printf("\tprintk(\"loaded kvm module (%s)\\n\");\n", version);
+ anon_inodes = 0
+}
+
+/^void kvm_exit/ { anon_inodes_exit = 1 }
+
+/\}/ && anon_inodes_exit {
+ print "\tkvm_exit_anon_inodes();";
+ print "\tpreempt_notifier_sys_exit();";
+ anon_inodes_exit = 0
+}
+
+/MODULE_AUTHOR/ {
+ printf("MODULE_INFO(version, \"%s\");\n", version)
+}
+
+/^static unsigned long vmcs_readl/ {
+ in_vmcs_read = 1
+}
+
+/ASM_VMX_VMREAD_RDX_RAX/ && in_vmcs_read {
+ printf("\tstart_special_insn();\n")
+}
+
+/return/ && in_vmcs_read {
+ printf("\tend_special_insn();\n");
+ in_vmcs_read = 0
+}
+
+/^static void vmcs_writel/ {
+ in_vmcs_write = 1
+}
+
+/ASM_VMX_VMWRITE_RAX_RDX/ && in_vmcs_write {
+ printf("\tstart_special_insn();\n")
+}
+
+/if/ && in_vmcs_write {
+ printf("\tend_special_insn();\n");
+ in_vmcs_write = 0
+}
+
+/^static void vmx_load_host_state/ {
+ vmx_load_host_state = 1
+}
+
+/vmcs_readl\(HOST_GS_BASE\)/ && vmx_load_host_state {
+ $0 = "\t\twrmsrl(MSR_GS_BASE, gsbase);";
+ vmx_load_host_state = 0
+}
+
+/atomic_inc\(&kvm->mm->mm_count\);/ { $0 = "//" $0 }
+
+/^\t\.fault = / {
+ fcn = gensub(/,/, "", "g", $3)
+ $0 = "\t.VMA_OPS_FAULT(fault) = VMA_OPS_FAULT_FUNC(" fcn "),"
+}
+
+{ sub(/linux\/mm_types\.h/, "linux/mm.h") }
+
+{ print }
+
+/kvm_x86_ops->run/ {
+ print "\tspecial_reload_dr7();"
+}
+
+/static void vcpu_put|static int __vcpu_run|static struct kvm_vcpu \*vmx_create_vcpu/ {
+ in_tricky_func = 1
+}
+
+/preempt_disable|get_cpu/ && in_tricky_func {
+ printf("\tin_special_section();\n");
+ in_tricky_func = 0
+}
+
+/unsigned long flags;/ && vmx_load_host_state {
+ print "\tunsigned long gsbase;"
+}
+
+/local_irq_save/ && vmx_load_host_state {
+ print "\t\tgsbase = vmcs_readl(HOST_GS_BASE);"
+}
+
diff --git a/kvm/kernel/i386.config b/kvm/kernel/i386.config
new file mode 100644
index 000000000..363ebe146
--- /dev/null
+++ b/kvm/kernel/i386.config
@@ -0,0 +1,1783 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.20
+# Sun Feb 18 19:15:49 2007
+#
+CONFIG_X86_32=y
+CONFIG_GENERIC_TIME=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_SEMAPHORE_SLEEPERS=y
+CONFIG_X86=y
+CONFIG_MMU=y
+CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_DMI=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_IPC_NS is not set
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_CPUSETS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_RELAY=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SHMEM=y
+CONFIG_SLAB=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_SLOB is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+
+#
+# Block layer
+#
+CONFIG_BLOCK=y
+CONFIG_LBD=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+
+#
+# Processor type and features
+#
+CONFIG_SMP=y
+# CONFIG_X86_PC is not set
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_VOYAGER is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+# CONFIG_X86_BIGSMP is not set
+# CONFIG_X86_VISWS is not set
+CONFIG_X86_GENERICARCH=y
+# CONFIG_X86_ES7000 is not set
+# CONFIG_PARAVIRT is not set
+CONFIG_X86_CYCLONE_TIMER=y
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+CONFIG_M686=y
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MCORE2 is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MEFFICEON is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MGEODEGX1 is not set
+# CONFIG_MGEODE_LX is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+CONFIG_X86_GENERIC=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_X86_PPRO_FENCE=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_CMPXCHG64=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_TSC=y
+CONFIG_HPET_TIMER=y
+CONFIG_HPET_EMULATE_RTC=y
+CONFIG_NR_CPUS=4
+CONFIG_SCHED_SMT=y
+CONFIG_SCHED_MC=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_BKL=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_MCE=y
+# CONFIG_X86_MCE_NONFATAL is not set
+CONFIG_X86_MCE_P4THERMAL=y
+CONFIG_VM86=y
+# CONFIG_TOSHIBA is not set
+# CONFIG_I8K is not set
+# CONFIG_X86_REBOOTFIXUPS is not set
+CONFIG_MICROCODE=m
+CONFIG_MICROCODE_OLD_INTERFACE=y
+CONFIG_X86_MSR=m
+CONFIG_X86_CPUID=m
+
+#
+# Firmware Drivers
+#
+CONFIG_EDD=m
+CONFIG_DELL_RBU=m
+CONFIG_DCDBAS=m
+# CONFIG_NOHIGHMEM is not set
+# CONFIG_HIGHMEM4G is not set
+CONFIG_HIGHMEM64G=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_HIGHMEM=y
+CONFIG_X86_PAE=y
+# CONFIG_NUMA is not set
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_RESOURCES_64BIT=y
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_HIGHPTE=y
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+# CONFIG_EFI is not set
+# CONFIG_IRQBALANCE is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_KEXEC=y
+# CONFIG_CRASH_DUMP is not set
+CONFIG_PHYSICAL_START=0x100000
+# CONFIG_RELOCATABLE is not set
+CONFIG_PHYSICAL_ALIGN=0x100000
+CONFIG_HOTPLUG_CPU=y
+CONFIG_COMPAT_VDSO=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+
+#
+# Power management options (ACPI, APM)
+#
+CONFIG_PM=y
+CONFIG_PM_LEGACY=y
+CONFIG_PM_DEBUG=y
+# CONFIG_DISABLE_CONSOLE_SUSPEND is not set
+# CONFIG_PM_TRACE is not set
+# CONFIG_PM_SYSFS_DEPRECATED is not set
+# CONFIG_SOFTWARE_SUSPEND is not set
+CONFIG_SUSPEND_SMP=y
+
+#
+# ACPI (Advanced Configuration and Power Interface) Support
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_SLEEP_PROC_FS=y
+# CONFIG_ACPI_SLEEP_PROC_SLEEP is not set
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_AC=m
+CONFIG_ACPI_BATTERY=m
+CONFIG_ACPI_BUTTON=m
+CONFIG_ACPI_VIDEO=m
+# CONFIG_ACPI_HOTKEY is not set
+CONFIG_ACPI_FAN=y
+# CONFIG_ACPI_DOCK is not set
+# CONFIG_ACPI_BAY is not set
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_HOTPLUG_CPU=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_ASUS=m
+CONFIG_ACPI_IBM=m
+CONFIG_ACPI_IBM_DOCK=y
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_BLACKLIST_YEAR=1999
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_X86_PM_TIMER=y
+CONFIG_ACPI_CONTAINER=y
+# CONFIG_ACPI_SBS is not set
+
+#
+# APM (Advanced Power Management) BIOS Support
+#
+CONFIG_APM=y
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
+CONFIG_APM_RTC_IS_GMT=y
+# CONFIG_APM_ALLOW_INTS is not set
+# CONFIG_APM_REAL_MODE_POWER_OFF is not set
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+
+#
+# CPUFreq processor drivers
+#
+CONFIG_X86_ACPI_CPUFREQ=m
+# CONFIG_X86_POWERNOW_K6 is not set
+CONFIG_X86_POWERNOW_K7=y
+CONFIG_X86_POWERNOW_K7_ACPI=y
+CONFIG_X86_POWERNOW_K8=y
+CONFIG_X86_POWERNOW_K8_ACPI=y
+# CONFIG_X86_GX_SUSPMOD is not set
+CONFIG_X86_SPEEDSTEP_CENTRINO=y
+CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y
+CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE=y
+CONFIG_X86_SPEEDSTEP_ICH=y
+CONFIG_X86_SPEEDSTEP_SMI=y
+CONFIG_X86_P4_CLOCKMOD=m
+# CONFIG_X86_CPUFREQ_NFORCE2 is not set
+CONFIG_X86_LONGRUN=y
+# CONFIG_X86_LONGHAUL is not set
+
+#
+# shared options
+#
+# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
+CONFIG_X86_SPEEDSTEP_LIB=y
+# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIEAER=y
+CONFIG_PCI_MSI=y
+# CONFIG_PCI_DEBUG is not set
+CONFIG_HT_IRQ=y
+CONFIG_ISA_DMA_API=y
+CONFIG_ISA=y
+# CONFIG_EISA is not set
+# CONFIG_MCA is not set
+# CONFIG_SCx200 is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+CONFIG_NET_KEY=m
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+# CONFIG_IP_ROUTE_MULTIPATH_CACHED is not set
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_TUNNEL=m
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+
+#
+# IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS transport protocol load balancing support
+#
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+
+#
+# IPVS scheduler
+#
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETLABEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NETFILTER_NETLINK_QUEUE=m
+CONFIG_NETFILTER_NETLINK_LOG=m
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+CONFIG_NETFILTER_XTABLES=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_ULOG=m
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+# CONFIG_DECNET is not set
+CONFIG_LLC=m
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_NET_SCH_CLK_JIFFIES is not set
+CONFIG_NET_SCH_CLK_GETTIMEOFDAY=y
+# CONFIG_NET_SCH_CLK_CPU is not set
+
+#
+# Queueing/Scheduling
+#
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_INGRESS=m
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_CLS_POLICE=y
+CONFIG_NET_CLS_IND=y
+CONFIG_NET_ESTIMATOR=y
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+CONFIG_FIB_RULES=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+CONFIG_CONNECTOR=m
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG is not set
+
+#
+# Protocols
+#
+CONFIG_ISAPNP=y
+# CONFIG_PNPBIOS is not set
+CONFIG_PNPACPI=y
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# Misc devices
+#
+# CONFIG_IBM_ASM is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_MSI_LAPTOP is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_BLK_DEV_IDEFLOPPY=y
+CONFIG_BLK_DEV_IDESCSI=m
+# CONFIG_BLK_DEV_IDEACPI is not set
+CONFIG_IDE_TASK_IOCTL=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+CONFIG_BLK_DEV_CMD640=y
+CONFIG_BLK_DEV_CMD640_ENHANCED=y
+CONFIG_BLK_DEV_IDEPNP=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_AEC62XX=y
+CONFIG_BLK_DEV_ALI15X3=y
+# CONFIG_WDC_ALI15X3 is not set
+CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_BLK_DEV_ATIIXP=y
+CONFIG_BLK_DEV_CMD64X=y
+CONFIG_BLK_DEV_TRIFLEX=y
+CONFIG_BLK_DEV_CY82C693=y
+CONFIG_BLK_DEV_CS5520=y
+CONFIG_BLK_DEV_CS5530=y
+CONFIG_BLK_DEV_CS5535=y
+CONFIG_BLK_DEV_HPT34X=y
+# CONFIG_HPT34X_AUTODMA is not set
+CONFIG_BLK_DEV_HPT366=y
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_IT8213 is not set
+CONFIG_BLK_DEV_IT821X=y
+# CONFIG_BLK_DEV_NS87415 is not set
+CONFIG_BLK_DEV_PDC202XX_OLD=y
+# CONFIG_PDC202XX_BURST is not set
+CONFIG_BLK_DEV_PDC202XX_NEW=y
+CONFIG_BLK_DEV_SVWKS=y
+CONFIG_BLK_DEV_SIIMAGE=y
+CONFIG_BLK_DEV_SIS5513=y
+CONFIG_BLK_DEV_SLC90E66=y
+# CONFIG_BLK_DEV_TRM290 is not set
+CONFIG_BLK_DEV_VIA82CXXX=y
+# CONFIG_BLK_DEV_TC86C001 is not set
+# CONFIG_IDE_ARM is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_IVB is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+CONFIG_RAID_ATTRS=m
+CONFIG_SCSI=m
+# CONFIG_SCSI_TGT is not set
+CONFIG_SCSI_NETLINK=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+CONFIG_CHR_DEV_SCH=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+# CONFIG_SCSI_SCAN_ASYNC is not set
+
+#
+# SCSI Transports
+#
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_ISCSI_ATTRS=m
+CONFIG_SCSI_SAS_ATTRS=m
+# CONFIG_SCSI_SAS_LIBSAS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_SRP is not set
+
+#
+# Serial ATA (prod) and Parallel ATA (experimental) drivers
+#
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_AHCI is not set
+# CONFIG_SATA_SVW is not set
+CONFIG_ATA_PIIX=m
+# CONFIG_SATA_MV is not set
+# CONFIG_SATA_NV is not set
+# CONFIG_PDC_ADMA is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_SX4 is not set
+# CONFIG_SATA_SIL is not set
+# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+# CONFIG_SATA_INIC162X is not set
+CONFIG_SATA_INTEL_COMBINED=y
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CS5535 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_ISAPNP is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_LEGACY is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_QDI is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+# CONFIG_PATA_WINBOND_VLB is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+# CONFIG_MD_RAID456 is not set
+CONFIG_MD_MULTIPATH=m
+CONFIG_MD_FAULTY=m
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_SPI is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+# CONFIG_MAC_EMUMOUSEBTN is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+CONFIG_BONDING=m
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_NET_SB1000 is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# PHY device support
+#
+
+#
+# Ethernet (10 or 100Mbit)
+#
+# CONFIG_NET_ETHERNET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+CONFIG_E1000=m
+CONFIG_E1000_NAPI=y
+# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SKY2 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_BNX2 is not set
+# CONFIG_QLA3XXX is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGB is not set
+# CONFIG_S2IO is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NETXEN_NIC is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+CONFIG_NETCONSOLE=m
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+CONFIG_NETPOLL_TRAP=y
+CONFIG_NET_POLL_CONTROLLER=y
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_FF_MEMLESS=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+CONFIG_INPUT_JOYDEV=m
+# CONFIG_INPUT_TSDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIO_RAW=m
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_GAMEPORT_EMU10K1=m
+CONFIG_GAMEPORT_FM801=m
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_ESPSERIAL is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_MOXA_SMARTIO_NEW is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_SYNCLINK_GT is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_PNP=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+# CONFIG_SERIAL_8250_FOURPORT is not set
+# CONFIG_SERIAL_8250_ACCENT is not set
+# CONFIG_SERIAL_8250_BOCA is not set
+# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set
+# CONFIG_SERIAL_8250_HUB6 is not set
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+
+#
+# IPMI
+#
+CONFIG_IPMI_HANDLER=m
+# CONFIG_IPMI_PANIC_EVENT is not set
+CONFIG_IPMI_DEVICE_INTERFACE=m
+CONFIG_IPMI_SI=m
+CONFIG_IPMI_WATCHDOG=m
+CONFIG_IPMI_POWEROFF=m
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+CONFIG_SOFT_WATCHDOG=m
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+CONFIG_ALIM1535_WDT=m
+CONFIG_ALIM7101_WDT=m
+# CONFIG_SC520_WDT is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+CONFIG_IBMASR=m
+# CONFIG_WAFER_WDT is not set
+CONFIG_I6300ESB_WDT=m
+CONFIG_I8XX_TCO=m
+# CONFIG_ITCO_WDT is not set
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_60XX_WDT is not set
+# CONFIG_SBC8360_WDT is not set
+# CONFIG_CPU5_WDT is not set
+# CONFIG_SMSC37B787_WDT is not set
+CONFIG_W83627HF_WDT=m
+# CONFIG_W83697HF_WDT is not set
+CONFIG_W83877F_WDT=m
+CONFIG_W83977F_WDT=m
+CONFIG_MACHZ_WDT=m
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+
+#
+# ISA-based Watchdog Cards
+#
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_WDT is not set
+
+#
+# PCI-based Watchdog Cards
+#
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_WDTPCI=m
+CONFIG_WDT_501_PCI=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_INTEL=y
+CONFIG_HW_RANDOM_AMD=y
+CONFIG_HW_RANDOM_GEODE=y
+CONFIG_HW_RANDOM_VIA=y
+CONFIG_NVRAM=y
+CONFIG_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_SONYPI is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_MWAVE is not set
+# CONFIG_PC8736x_GPIO is not set
+# CONFIG_NSC_GPIO is not set
+# CONFIG_CS5535_GPIO is not set
+# CONFIG_RAW_DRIVER is not set
+CONFIG_HPET=y
+# CONFIG_HPET_RTC_IRQ is not set
+# CONFIG_HPET_MMAP is not set
+CONFIG_HANGCHECK_TIMER=m
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+CONFIG_I2C_CHARDEV=m
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+CONFIG_I2C_ALGOPCF=m
+CONFIG_I2C_ALGOPCA=m
+
+#
+# I2C Hardware Bus support
+#
+CONFIG_I2C_ALI1535=m
+CONFIG_I2C_ALI1563=m
+CONFIG_I2C_ALI15X3=m
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD756_S4882=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_I810=m
+CONFIG_I2C_PIIX4=m
+CONFIG_I2C_NFORCE2=m
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PARPORT_LIGHT=m
+# CONFIG_I2C_PASEMI is not set
+CONFIG_I2C_PROSAVAGE=m
+CONFIG_I2C_SAVAGE4=m
+# CONFIG_SCx200_ACB is not set
+CONFIG_I2C_SIS5595=m
+CONFIG_I2C_SIS630=m
+CONFIG_I2C_SIS96X=m
+CONFIG_I2C_STUB=m
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+CONFIG_I2C_VOODOO3=m
+CONFIG_I2C_PCA_ISA=m
+
+#
+# Miscellaneous I2C Chip support
+#
+CONFIG_SENSORS_DS1337=m
+CONFIG_SENSORS_DS1374=m
+CONFIG_SENSORS_EEPROM=m
+CONFIG_SENSORS_PCF8574=m
+CONFIG_SENSORS_PCA9539=m
+CONFIG_SENSORS_PCF8591=m
+CONFIG_SENSORS_MAX6875=m
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_VID is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
+CONFIG_VIDEO_SELECT=y
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_DEVICE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# HID Devices
+#
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+# CONFIG_USB is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+CONFIG_EDAC=y
+
+#
+# Reporting subsystems
+#
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_AMD76X=m
+CONFIG_EDAC_E7XXX=m
+CONFIG_EDAC_E752X=m
+CONFIG_EDAC_I82875P=m
+CONFIG_EDAC_I82860=m
+CONFIG_EDAC_R82600=m
+CONFIG_EDAC_POLL=y
+
+#
+# Real Time Clock
+#
+CONFIG_RTC_LIB=m
+CONFIG_RTC_CLASS=m
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=m
+CONFIG_RTC_INTF_PROC=m
+CONFIG_RTC_INTF_DEV=m
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+
+#
+# RTC drivers
+#
+CONFIG_RTC_DRV_X1205=m
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+CONFIG_RTC_DRV_DS1672=m
+# CONFIG_RTC_DRV_DS1742 is not set
+CONFIG_RTC_DRV_PCF8563=m
+# CONFIG_RTC_DRV_PCF8583 is not set
+CONFIG_RTC_DRV_RS5C372=m
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_TEST is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
+
+#
+# Auxiliary Display support
+#
+
+#
+# Virtualization
+#
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+CONFIG_EXT3_FS=m
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=m
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=m
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_RAMFS=y
+CONFIG_CONFIGFS_FS=m
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_DIRECTIO=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_RPCSEC_GSS_SPKM3=m
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=m
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+CONFIG_NLS_CODEPAGE_862=m
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+CONFIG_NLS_ISO8859_8=m
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=m
+
+#
+# Distributed Lock Manager
+#
+# CONFIG_DLM is not set
+
+#
+# Instrumentation Support
+#
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_KPROBES=y
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_DETECT_SOFTLOCKUP=y
+CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_LKDTM is not set
+CONFIG_EARLY_PRINTK=y
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_RODATA is not set
+CONFIG_4KSTACKS=y
+CONFIG_X86_FIND_SMP_CONFIG=y
+CONFIG_X86_MPPARSE=y
+CONFIG_DOUBLEFAULT=y
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_NETWORK_XFRM=y
+CONFIG_SECURITY_CAPABILITIES=y
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_TGR192=m
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_AES_586=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
+CONFIG_CRYPTO_DEV_PADLOCK=m
+CONFIG_CRYPTO_DEV_PADLOCK_AES=m
+CONFIG_CRYPTO_DEV_PADLOCK_SHA=m
+CONFIG_CRYPTO_DEV_GEODE=m
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC16=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=m
+CONFIG_TEXTSEARCH_BM=m
+CONFIG_TEXTSEARCH_FSM=m
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_X86_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
+CONFIG_KTIME_SCALAR=y
diff --git a/kvm/kernel/include/asm-x86/cmpxchg.h b/kvm/kernel/include/asm-x86/cmpxchg.h
new file mode 100644
index 000000000..4258b93b1
--- /dev/null
+++ b/kvm/kernel/include/asm-x86/cmpxchg.h
@@ -0,0 +1,5 @@
+/*
+ * Empty file to satisfy #include <linux/cmpxchg.h> for older kernels.
+ */
+
+
diff --git a/kvm/kernel/include/linux/anon_inodes.h b/kvm/kernel/include/linux/anon_inodes.h
new file mode 100644
index 000000000..b2e1ba325
--- /dev/null
+++ b/kvm/kernel/include/linux/anon_inodes.h
@@ -0,0 +1,16 @@
+/*
+ * include/linux/anon_inodes.h
+ *
+ * Copyright (C) 2007 Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#ifndef _LINUX_ANON_INODES_H
+#define _LINUX_ANON_INODES_H
+
+int anon_inode_getfd(int *pfd, struct inode **pinode, struct file **pfile,
+ const char *name, const struct file_operations *fops,
+ void *priv);
+
+#endif /* _LINUX_ANON_INODES_H */
+
diff --git a/kvm/kernel/include/linux/magic.h b/kvm/kernel/include/linux/magic.h
new file mode 100644
index 000000000..a9c6567fe
--- /dev/null
+++ b/kvm/kernel/include/linux/magic.h
@@ -0,0 +1,41 @@
+#ifndef __LINUX_MAGIC_H__
+#define __LINUX_MAGIC_H__
+
+#define ADFS_SUPER_MAGIC 0xadf5
+#define AFFS_SUPER_MAGIC 0xadff
+#define AFS_SUPER_MAGIC 0x5346414F
+#define AUTOFS_SUPER_MAGIC 0x0187
+#define CODA_SUPER_MAGIC 0x73757245
+#define EFS_SUPER_MAGIC 0x414A53
+#define EXT2_SUPER_MAGIC 0xEF53
+#define EXT3_SUPER_MAGIC 0xEF53
+#define EXT4_SUPER_MAGIC 0xEF53
+#define HPFS_SUPER_MAGIC 0xf995e849
+#define ISOFS_SUPER_MAGIC 0x9660
+#define JFFS2_SUPER_MAGIC 0x72b6
+#define KVMFS_SUPER_MAGIC 0x19700426
+
+#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
+#define MINIX3_SUPER_MAGIC 0x4d5a /* minix V3 fs */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+#define NCP_SUPER_MAGIC 0x564c /* Guess, what 0x564c is :-) */
+#define NFS_SUPER_MAGIC 0x6969
+#define OPENPROM_SUPER_MAGIC 0x9fa1
+#define PROC_SUPER_MAGIC 0x9fa0
+#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */
+
+#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */
+ /* used by file system utilities that
+ look at the superblock, etc. */
+#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
+#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
+
+#define SMB_SUPER_MAGIC 0x517B
+#define USBDEVICE_SUPER_MAGIC 0x9fa2
+
+#endif /* __LINUX_MAGIC_H__ */
diff --git a/kvm/kernel/include/linux/mutex.h b/kvm/kernel/include/linux/mutex.h
new file mode 100644
index 000000000..71b2ae109
--- /dev/null
+++ b/kvm/kernel/include/linux/mutex.h
@@ -0,0 +1,5 @@
+/*
+ * Empty file to satisfy #include <linux/mutex.h> for older kernels.
+ */
+
+
diff --git a/kvm/kernel/kvm-kmod.spec b/kvm/kernel/kvm-kmod.spec
new file mode 100644
index 000000000..b56fdf1be
--- /dev/null
+++ b/kvm/kernel/kvm-kmod.spec
@@ -0,0 +1,50 @@
+%define kmod_name kvm
+
+Name: kvm-kmod
+Version: 0.0
+Release: 0
+Summary: %{kmod_name} kernel module
+
+Group: System Environment/Kernel
+License: GPL
+URL: http://www.qumranet.com
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
+
+ExclusiveArch: i386 x86_64
+
+%description
+This kernel module provides support for virtual machines using hardware support
+(Intel VT or AMD SVM).
+
+%prep
+
+%build
+
+rm -rf %{buildroot}
+
+%install
+
+%define kverrel unknown
+%define moddir /lib/modules/%{kverrel}/extra
+mkdir -p %{buildroot}/%{moddir}
+cp %{objdir}/%{kmod_name}.ko %{objdir}/%{kmod_name}-intel.ko %{objdir}/%{kmod_name}-amd.ko %{buildroot}/%{moddir}
+chmod u+x %{buildroot}/%{moddir}/%{kmod_name}*.ko
+
+%post
+
+depmod %{kverrel}
+
+%postun
+
+depmod %{kverrel}
+
+%clean
+%{__rm} -rf %{buildroot}
+
+%files
+%{moddir}/%{kmod_name}.ko
+%{moddir}/%{kmod_name}-amd.ko
+%{moddir}/%{kmod_name}-intel.ko
+
+
+%changelog
diff --git a/kvm/kernel/preempt.c b/kvm/kernel/preempt.c
new file mode 100644
index 000000000..8bb040536
--- /dev/null
+++ b/kvm/kernel/preempt.c
@@ -0,0 +1,254 @@
+
+#ifndef CONFIG_PREEMPT_NOTIFIERS
+
+#include <linux/sched.h>
+#include <linux/percpu.h>
+
+static DEFINE_SPINLOCK(pn_lock);
+static LIST_HEAD(pn_list);
+static DEFINE_PER_CPU(int, notifier_enabled);
+static DEFINE_PER_CPU(struct task_struct *, last_tsk);
+
+#define dprintk(fmt) do { \
+ if (0) \
+ printk("%s (%d/%d): " fmt, __FUNCTION__, \
+ current->pid, raw_smp_processor_id()); \
+ } while (0)
+
+static void preempt_enable_notifiers(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (per_cpu(notifier_enabled, cpu))
+ return;
+
+ dprintk("\n");
+ per_cpu(notifier_enabled, cpu) = 1;
+ asm volatile ("mov %0, %%db0" : : "r"(schedule));
+ asm volatile ("mov %0, %%db7" : : "r"(0x702ul));
+}
+
+void special_reload_dr7(void)
+{
+ asm volatile ("mov %0, %%db7" : : "r"(0x702ul));
+}
+EXPORT_SYMBOL_GPL(special_reload_dr7);
+
+static void preempt_disable_notifiers(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (!per_cpu(notifier_enabled, cpu))
+ return;
+
+ dprintk("\n");
+ per_cpu(notifier_enabled, cpu) = 0;
+ asm volatile ("mov %0, %%db7" : : "r"(0x400ul));
+}
+
+static void __attribute__((used)) preempt_notifier_trigger(void)
+{
+ struct preempt_notifier *pn;
+ int cpu = raw_smp_processor_id();
+ int found = 0;
+ unsigned long flags;
+
+ dprintk(" - in\n");
+ //dump_stack();
+ spin_lock_irqsave(&pn_lock, flags);
+ list_for_each_entry(pn, &pn_list, link)
+ if (pn->tsk == current) {
+ found = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&pn_lock, flags);
+ preempt_disable_notifiers();
+ if (found) {
+ dprintk("sched_out\n");
+ pn->ops->sched_out(pn, NULL);
+ per_cpu(last_tsk, cpu) = NULL;
+ }
+ dprintk(" - out\n");
+}
+
+unsigned long orig_int1_handler;
+
+#ifdef CONFIG_X86_64
+
+#define SAVE_REGS \
+ "push %rax; push %rbx; push %rcx; push %rdx; " \
+ "push %rsi; push %rdi; push %rbp; " \
+ "push %r8; push %r9; push %r10; push %r11; " \
+ "push %r12; push %r13; push %r14; push %r15"
+
+#define RESTORE_REGS \
+ "pop %r15; pop %r14; pop %r13; pop %r12; " \
+ "pop %r11; pop %r10; pop %r9; pop %r8; " \
+ "pop %rbp; pop %rdi; pop %rsi; " \
+ "pop %rdx; pop %rcx; pop %rbx; pop %rax "
+
+#define TMP "%rax"
+
+#else
+
+#define SAVE_REGS "pusha"
+#define RESTORE_REGS "popa"
+#define TMP "%eax"
+
+#endif
+
+asm ("pn_int1_handler: \n\t"
+ "push " TMP " \n\t"
+ "mov %db6, " TMP " \n\t"
+ "test $1, " TMP " \n\t"
+ "pop " TMP " \n\t"
+ "jz .Lnotme \n\t"
+ SAVE_REGS "\n\t"
+ "call preempt_notifier_trigger \n\t"
+ RESTORE_REGS "\n\t"
+#ifdef CONFIG_X86_64
+ "orq $0x10000, 16(%rsp) \n\t"
+ "iretq \n\t"
+#else
+ "orl $0x10000, 8(%esp) \n\t"
+ "iret \n\t"
+#endif
+ ".Lnotme: \n\t"
+#ifdef CONFIG_X86_64
+ "jmpq *orig_int1_handler\n\t"
+#else
+ "jmpl *orig_int1_handler\n\t"
+#endif
+ );
+
+void in_special_section(void)
+{
+ struct preempt_notifier *pn;
+ int cpu = raw_smp_processor_id();
+ int found = 0;
+ unsigned long flags;
+
+ if (per_cpu(last_tsk, cpu) == current)
+ return;
+
+ dprintk(" - in\n");
+ spin_lock_irqsave(&pn_lock, flags);
+ list_for_each_entry(pn, &pn_list, link)
+ if (pn->tsk == current) {
+ found = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&pn_lock, flags);
+ if (found) {
+ dprintk("\n");
+ per_cpu(last_tsk, cpu) = current;
+ pn->ops->sched_in(pn, cpu);
+ preempt_enable_notifiers();
+ }
+ dprintk(" - out\n");
+}
+EXPORT_SYMBOL_GPL(in_special_section);
+
+void start_special_insn(void)
+{
+ preempt_disable();
+ in_special_section();
+}
+EXPORT_SYMBOL_GPL(start_special_insn);
+
+void end_special_insn(void)
+{
+ preempt_enable();
+}
+EXPORT_SYMBOL_GPL(end_special_insn);
+
+void preempt_notifier_register(struct preempt_notifier *notifier)
+{
+ int cpu = get_cpu();
+ unsigned long flags;
+
+ dprintk(" - in\n");
+ spin_lock_irqsave(&pn_lock, flags);
+ preempt_enable_notifiers();
+ notifier->tsk = current;
+ list_add(&notifier->link, &pn_list);
+ spin_unlock_irqrestore(&pn_lock, flags);
+ per_cpu(last_tsk, cpu) = current;
+ put_cpu();
+ dprintk(" - out\n");
+}
+
+void preempt_notifier_unregister(struct preempt_notifier *notifier)
+{
+ int cpu = get_cpu();
+ unsigned long flags;
+
+ dprintk(" - in\n");
+ spin_lock_irqsave(&pn_lock, flags);
+ list_del(&notifier->link);
+ spin_unlock_irqrestore(&pn_lock, flags);
+ per_cpu(last_tsk, cpu) = NULL;
+ preempt_disable_notifiers();
+ put_cpu();
+ dprintk(" - out\n");
+}
+
+struct intr_gate {
+ u16 offset0;
+ u16 segment;
+ u16 junk;
+ u16 offset1;
+#ifdef CONFIG_X86_64
+ u32 offset2;
+ u32 blah;
+#endif
+} __attribute__((packed));
+
+struct idt_desc {
+ u16 limit;
+ struct intr_gate *gates;
+} __attribute__((packed));
+
+static struct intr_gate orig_int1_gate;
+
+void pn_int1_handler(void);
+
+void preempt_notifier_sys_init(void)
+{
+ struct idt_desc idt_desc;
+ struct intr_gate *int1_gate;
+
+ printk("kvm: emulating preempt notifiers;"
+ " do not benchmark on this machine\n");
+ dprintk("\n");
+ asm ("sidt %0" : "=m"(idt_desc));
+ int1_gate = &idt_desc.gates[1];
+ orig_int1_gate = *int1_gate;
+ orig_int1_handler = int1_gate->offset0
+ | ((u32)int1_gate->offset1 << 16);
+#ifdef CONFIG_X86_64
+ orig_int1_handler |= (u64)int1_gate->offset2 << 32;
+#endif
+ int1_gate->offset0 = (unsigned long)pn_int1_handler;
+ int1_gate->offset1 = (unsigned long)pn_int1_handler >> 16;
+#ifdef CONFIG_X86_64
+ int1_gate->offset2 = (unsigned long)pn_int1_handler >> 32;
+#endif
+}
+
+static void do_disable(void *blah)
+{
+ preempt_disable_notifiers();
+}
+
+void preempt_notifier_sys_exit(void)
+{
+ struct idt_desc idt_desc;
+
+ dprintk("\n");
+ on_each_cpu(do_disable, NULL, 1, 1);
+ asm ("sidt %0" : "=m"(idt_desc));
+ idt_desc.gates[1] = orig_int1_gate;
+}
+
+#endif
diff --git a/kvm/kernel/vmx-debug.c b/kvm/kernel/vmx-debug.c
new file mode 100644
index 000000000..29316a0e5
--- /dev/null
+++ b/kvm/kernel/vmx-debug.c
@@ -0,0 +1,1078 @@
+/*
+ * Kernel-based Virtual Machine driver for Linux
+ *
+ * This module enables machines with Intel VT-x extensions to run virtual
+ * machines without emulation or binary translation.
+ *
+ * Debug support
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Yaniv Kamay <yaniv@qumranet.com>
+ * Avi Kivity <avi@qumranet.com>
+ *
+ */
+
+#include <linux/highmem.h>
+
+#include <linux/kvm_host.h>
+#include "debug.h"
+
+#ifdef KVM_DEBUG
+
+static const char *vmx_msr_name[] = {
+ "MSR_EFER", "MSR_STAR", "MSR_CSTAR",
+ "MSR_KERNEL_GS_BASE", "MSR_SYSCALL_MASK", "MSR_LSTAR"
+};
+
+#define NR_VMX_MSR (sizeof(vmx_msr_name) / sizeof(char*))
+
+static unsigned long vmcs_readl(unsigned long field)
+{
+ unsigned long value;
+
+ asm volatile (ASM_VMX_VMREAD_RDX_RAX
+ : "=a"(value) : "d"(field) : "cc");
+ return value;
+}
+
+static u16 vmcs_read16(unsigned long field)
+{
+ return vmcs_readl(field);
+}
+
+static u32 vmcs_read32(unsigned long field)
+{
+ return vmcs_readl(field);
+}
+
+static u64 vmcs_read64(unsigned long field)
+{
+#ifdef CONFIG_X86_64
+ return vmcs_readl(field);
+#else
+ return vmcs_readl(field) | ((u64)vmcs_readl(field+1) << 32);
+#endif
+}
+
+void show_msrs(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ for (i = 0; i < NR_VMX_MSR; ++i) {
+ vcpu_printf(vcpu, "%s: %s=0x%llx\n",
+ __FUNCTION__,
+ vmx_msr_name[i],
+ vcpu->guest_msrs[i].data);
+ }
+}
+
+void show_code(struct kvm_vcpu *vcpu)
+{
+ gva_t rip = vmcs_readl(GUEST_RIP);
+ u8 code[50];
+ char buf[30 + 3 * sizeof code];
+ int i;
+
+ if (!is_long_mode(vcpu))
+ rip += vmcs_readl(GUEST_CS_BASE);
+
+ kvm_read_guest(vcpu, rip, sizeof code, code);
+ for (i = 0; i < sizeof code; ++i)
+ sprintf(buf + i * 3, " %02x", code[i]);
+ vcpu_printf(vcpu, "code: %lx%s\n", rip, buf);
+}
+
+struct gate_struct {
+ u16 offset_low;
+ u16 segment;
+ unsigned ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
+ u16 offset_middle;
+ u32 offset_high;
+ u32 zero1;
+} __attribute__((packed));
+
+void show_irq(struct kvm_vcpu *vcpu, int irq)
+{
+ unsigned long idt_base = vmcs_readl(GUEST_IDTR_BASE);
+ unsigned long idt_limit = vmcs_readl(GUEST_IDTR_LIMIT);
+ struct gate_struct gate;
+
+ if (!is_long_mode(vcpu))
+ vcpu_printf(vcpu, "%s: not in long mode\n", __FUNCTION__);
+
+ if (!is_long_mode(vcpu) || idt_limit < irq * sizeof(gate)) {
+ vcpu_printf(vcpu, "%s: 0x%x read_guest err\n",
+ __FUNCTION__,
+ irq);
+ return;
+ }
+
+ if (kvm_read_guest(vcpu, idt_base + irq * sizeof(gate), sizeof(gate), &gate) != sizeof(gate)) {
+ vcpu_printf(vcpu, "%s: 0x%x read_guest err\n",
+ __FUNCTION__,
+ irq);
+ return;
+ }
+ vcpu_printf(vcpu, "%s: 0x%x handler 0x%llx\n",
+ __FUNCTION__,
+ irq,
+ ((u64)gate.offset_high << 32) |
+ ((u64)gate.offset_middle << 16) |
+ gate.offset_low);
+}
+
+void show_page(struct kvm_vcpu *vcpu,
+ gva_t addr)
+{
+ u64 *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+ if (!buf)
+ return;
+
+ addr &= PAGE_MASK;
+ if (kvm_read_guest(vcpu, addr, PAGE_SIZE, buf)) {
+ int i;
+ for (i = 0; i < PAGE_SIZE / sizeof(u64) ; i++) {
+ u8 *ptr = (u8*)&buf[i];
+ int j;
+ vcpu_printf(vcpu, " 0x%16.16lx:",
+ addr + i * sizeof(u64));
+ for (j = 0; j < sizeof(u64) ; j++)
+ vcpu_printf(vcpu, " 0x%2.2x", ptr[j]);
+ vcpu_printf(vcpu, "\n");
+ }
+ }
+ kfree(buf);
+}
+
+void show_u64(struct kvm_vcpu *vcpu, gva_t addr)
+{
+ u64 buf;
+
+ if (kvm_read_guest(vcpu, addr, sizeof(u64), &buf) == sizeof(u64)) {
+ u8 *ptr = (u8*)&buf;
+ int j;
+ vcpu_printf(vcpu, " 0x%16.16lx:", addr);
+ for (j = 0; j < sizeof(u64) ; j++)
+ vcpu_printf(vcpu, " 0x%2.2x", ptr[j]);
+ vcpu_printf(vcpu, "\n");
+ }
+}
+
+#define IA32_DEBUGCTL_RESERVED_BITS 0xfffffffffffffe3cULL
+
+static int is_canonical(unsigned long addr)
+{
+ return addr == ((long)addr << 16) >> 16;
+}
+
+int vm_entry_test_guest(struct kvm_vcpu *vcpu)
+{
+ unsigned long cr0;
+ unsigned long cr4;
+ unsigned long cr3;
+ unsigned long dr7;
+ u64 ia32_debugctl;
+ unsigned long sysenter_esp;
+ unsigned long sysenter_eip;
+ unsigned long rflags;
+
+ int long_mode;
+ int virtual8086;
+
+ #define RFLAGS_VM (1 << 17)
+ #define RFLAGS_RF (1 << 9)
+
+
+ #define VIR8086_SEG_BASE_TEST(seg)\
+ if (vmcs_readl(GUEST_##seg##_BASE) != \
+ (unsigned long)vmcs_read16(GUEST_##seg##_SELECTOR) << 4) {\
+ vcpu_printf(vcpu, "%s: "#seg" base 0x%lx in "\
+ "virtual8086 is not "#seg" selector 0x%x"\
+ " shifted right 4 bits\n",\
+ __FUNCTION__,\
+ vmcs_readl(GUEST_##seg##_BASE),\
+ vmcs_read16(GUEST_##seg##_SELECTOR));\
+ return 0;\
+ }
+
+ #define VIR8086_SEG_LIMIT_TEST(seg)\
+ if (vmcs_readl(GUEST_##seg##_LIMIT) != 0x0ffff) { \
+ vcpu_printf(vcpu, "%s: "#seg" limit 0x%lx in "\
+ "virtual8086 is not 0xffff\n",\
+ __FUNCTION__,\
+ vmcs_readl(GUEST_##seg##_LIMIT));\
+ return 0;\
+ }
+
+ #define VIR8086_SEG_AR_TEST(seg)\
+ if (vmcs_read32(GUEST_##seg##_AR_BYTES) != 0x0f3) { \
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x in "\
+ "virtual8086 is not 0xf3\n",\
+ __FUNCTION__,\
+ vmcs_read32(GUEST_##seg##_AR_BYTES));\
+ return 0;\
+ }
+
+
+ cr0 = vmcs_readl(GUEST_CR0);
+
+ if (!(cr0 & CR0_PG_MASK)) {
+ vcpu_printf(vcpu, "%s: cr0 0x%lx, PG is not set\n",
+ __FUNCTION__, cr0);
+ return 0;
+ }
+
+ if (!(cr0 & CR0_PE_MASK)) {
+ vcpu_printf(vcpu, "%s: cr0 0x%lx, PE is not set\n",
+ __FUNCTION__, cr0);
+ return 0;
+ }
+
+ if (!(cr0 & CR0_NE_MASK)) {
+ vcpu_printf(vcpu, "%s: cr0 0x%lx, NE is not set\n",
+ __FUNCTION__, cr0);
+ return 0;
+ }
+
+ if (!(cr0 & CR0_WP_MASK)) {
+ vcpu_printf(vcpu, "%s: cr0 0x%lx, WP is not set\n",
+ __FUNCTION__, cr0);
+ }
+
+ cr4 = vmcs_readl(GUEST_CR4);
+
+ if (!(cr4 & CR4_VMXE_MASK)) {
+ vcpu_printf(vcpu, "%s: cr4 0x%lx, VMXE is not set\n",
+ __FUNCTION__, cr4);
+ return 0;
+ }
+
+ if (!(cr4 & CR4_PAE_MASK)) {
+ vcpu_printf(vcpu, "%s: cr4 0x%lx, PAE is not set\n",
+ __FUNCTION__, cr4);
+ }
+
+ ia32_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL);
+
+ if (ia32_debugctl & IA32_DEBUGCTL_RESERVED_BITS ) {
+ vcpu_printf(vcpu, "%s: ia32_debugctl 0x%llx, reserve bits\n",
+ __FUNCTION__, ia32_debugctl);
+ return 0;
+ }
+
+ long_mode = is_long_mode(vcpu);
+
+ if (long_mode) {
+ }
+
+ if ( long_mode && !(cr4 & CR4_PAE_MASK)) {
+ vcpu_printf(vcpu, "%s: long mode and not PAE\n",
+ __FUNCTION__);
+ return 0;
+ }
+
+ cr3 = vmcs_readl(GUEST_CR3);
+
+ if (cr3 & CR3_L_MODE_RESEVED_BITS) {
+ vcpu_printf(vcpu, "%s: cr3 0x%lx, reserved bits\n",
+ __FUNCTION__, cr3);
+ return 0;
+ }
+
+ if ( !long_mode && (cr4 & CR4_PAE_MASK)) {
+ /* check the 4 PDPTEs for reserved bits */
+ unsigned long pdpt_pfn = cr3 >> PAGE_SHIFT;
+ int i;
+ u64 pdpte;
+ unsigned offset = (cr3 & (PAGE_SIZE-1)) >> 5;
+ u64 *pdpt = kmap_atomic(pfn_to_page(pdpt_pfn), KM_USER0);
+
+ for (i = 0; i < 4; ++i) {
+ pdpte = pdpt[offset + i];
+ if ((pdpte & 1) && (pdpte & 0xfffffff0000001e6ull))
+ break;
+ }
+
+ kunmap_atomic(pdpt, KM_USER0);
+
+ if (i != 4) {
+ vcpu_printf(vcpu, "%s: pae cr3[%d] 0x%llx, reserved bits\n",
+ __FUNCTION__, i, pdpte);
+ return 0;
+ }
+ }
+
+ dr7 = vmcs_readl(GUEST_DR7);
+
+ if (dr7 & ~((1ULL << 32) - 1)) {
+ vcpu_printf(vcpu, "%s: dr7 0x%lx, reserved bits\n",
+ __FUNCTION__, dr7);
+ return 0;
+ }
+
+ sysenter_esp = vmcs_readl(GUEST_SYSENTER_ESP);
+
+ if (!is_canonical(sysenter_esp)) {
+ vcpu_printf(vcpu, "%s: sysenter_esp 0x%lx, not canonical\n",
+ __FUNCTION__, sysenter_esp);
+ return 0;
+ }
+
+ sysenter_eip = vmcs_readl(GUEST_SYSENTER_EIP);
+
+ if (!is_canonical(sysenter_eip)) {
+ vcpu_printf(vcpu, "%s: sysenter_eip 0x%lx, not canonical\n",
+ __FUNCTION__, sysenter_eip);
+ return 0;
+ }
+
+ rflags = vmcs_readl(GUEST_RFLAGS);
+ virtual8086 = rflags & RFLAGS_VM;
+
+
+ if (vmcs_read16(GUEST_TR_SELECTOR) & SELECTOR_TI_MASK) {
+ vcpu_printf(vcpu, "%s: tr selctor 0x%x, TI is set\n",
+ __FUNCTION__, vmcs_read16(GUEST_TR_SELECTOR));
+ return 0;
+ }
+
+ if (!(vmcs_read32(GUEST_LDTR_AR_BYTES) & AR_UNUSABLE_MASK) &&
+ vmcs_read16(GUEST_LDTR_SELECTOR) & SELECTOR_TI_MASK) {
+ vcpu_printf(vcpu, "%s: ldtr selctor 0x%x,"
+ " is usable and TI is set\n",
+ __FUNCTION__, vmcs_read16(GUEST_LDTR_SELECTOR));
+ return 0;
+ }
+
+ if (!virtual8086 &&
+ (vmcs_read16(GUEST_SS_SELECTOR) & SELECTOR_RPL_MASK) !=
+ (vmcs_read16(GUEST_CS_SELECTOR) & SELECTOR_RPL_MASK)) {
+ vcpu_printf(vcpu, "%s: ss selctor 0x%x cs selctor 0x%x,"
+ " not same RPL\n",
+ __FUNCTION__,
+ vmcs_read16(GUEST_SS_SELECTOR),
+ vmcs_read16(GUEST_CS_SELECTOR));
+ return 0;
+ }
+
+ if (virtual8086) {
+ VIR8086_SEG_BASE_TEST(CS);
+ VIR8086_SEG_BASE_TEST(SS);
+ VIR8086_SEG_BASE_TEST(DS);
+ VIR8086_SEG_BASE_TEST(ES);
+ VIR8086_SEG_BASE_TEST(FS);
+ VIR8086_SEG_BASE_TEST(GS);
+ }
+
+ if (!is_canonical(vmcs_readl(GUEST_TR_BASE)) ||
+ !is_canonical(vmcs_readl(GUEST_FS_BASE)) ||
+ !is_canonical(vmcs_readl(GUEST_GS_BASE)) ) {
+ vcpu_printf(vcpu, "%s: TR 0x%lx FS 0x%lx or GS 0x%lx base"
+ " is not canonical\n",
+ __FUNCTION__,
+ vmcs_readl(GUEST_TR_BASE),
+ vmcs_readl(GUEST_FS_BASE),
+ vmcs_readl(GUEST_GS_BASE));
+ return 0;
+
+ }
+
+ if (!(vmcs_read32(GUEST_LDTR_AR_BYTES) & AR_UNUSABLE_MASK) &&
+ !is_canonical(vmcs_readl(GUEST_LDTR_BASE))) {
+ vcpu_printf(vcpu, "%s: LDTR base 0x%lx, usable and is not"
+ " canonical\n",
+ __FUNCTION__,
+ vmcs_readl(GUEST_LDTR_BASE));
+ return 0;
+ }
+
+ if ((vmcs_readl(GUEST_CS_BASE) & ~((1ULL << 32) - 1))) {
+ vcpu_printf(vcpu, "%s: CS base 0x%lx, not all bits 63-32"
+ " are zero\n",
+ __FUNCTION__,
+ vmcs_readl(GUEST_CS_BASE));
+ return 0;
+ }
+
+ #define SEG_BASE_TEST(seg)\
+ if ( !(vmcs_read32(GUEST_##seg##_AR_BYTES) & AR_UNUSABLE_MASK) &&\
+ (vmcs_readl(GUEST_##seg##_BASE) & ~((1ULL << 32) - 1))) {\
+ vcpu_printf(vcpu, "%s: "#seg" base 0x%lx, is usable and not"\
+ " all bits 63-32 are zero\n",\
+ __FUNCTION__,\
+ vmcs_readl(GUEST_##seg##_BASE));\
+ return 0;\
+ }
+ SEG_BASE_TEST(SS);
+ SEG_BASE_TEST(DS);
+ SEG_BASE_TEST(ES);
+
+ if (virtual8086) {
+ VIR8086_SEG_LIMIT_TEST(CS);
+ VIR8086_SEG_LIMIT_TEST(SS);
+ VIR8086_SEG_LIMIT_TEST(DS);
+ VIR8086_SEG_LIMIT_TEST(ES);
+ VIR8086_SEG_LIMIT_TEST(FS);
+ VIR8086_SEG_LIMIT_TEST(GS);
+ }
+
+ if (virtual8086) {
+ VIR8086_SEG_AR_TEST(CS);
+ VIR8086_SEG_AR_TEST(SS);
+ VIR8086_SEG_AR_TEST(DS);
+ VIR8086_SEG_AR_TEST(ES);
+ VIR8086_SEG_AR_TEST(FS);
+ VIR8086_SEG_AR_TEST(GS);
+ } else {
+
+ u32 cs_ar = vmcs_read32(GUEST_CS_AR_BYTES);
+ u32 ss_ar = vmcs_read32(GUEST_SS_AR_BYTES);
+ u32 tr_ar = vmcs_read32(GUEST_TR_AR_BYTES);
+ u32 ldtr_ar = vmcs_read32(GUEST_LDTR_AR_BYTES);
+
+ #define SEG_G_TEST(seg) { \
+ u32 lim = vmcs_read32(GUEST_##seg##_LIMIT); \
+ u32 ar = vmcs_read32(GUEST_##seg##_AR_BYTES); \
+ int err = 0; \
+ if (((lim & ~PAGE_MASK) != ~PAGE_MASK) && (ar & AR_G_MASK)) \
+ err = 1; \
+ if ((lim & ~((1u << 20) - 1)) && !(ar & AR_G_MASK)) \
+ err = 1; \
+ if (err) { \
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x, G err. lim" \
+ " is 0x%x\n", \
+ __FUNCTION__, \
+ ar, lim); \
+ return 0; \
+ } \
+ }
+
+
+ if (!(cs_ar & AR_TYPE_ACCESSES_MASK)) {
+ vcpu_printf(vcpu, "%s: cs AR 0x%x, accesses is clear\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+ }
+
+ if (!(cs_ar & AR_TYPE_CODE_MASK)) {
+ vcpu_printf(vcpu, "%s: cs AR 0x%x, code is clear\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+ }
+
+ if (!(cs_ar & AR_S_MASK)) {
+ vcpu_printf(vcpu, "%s: cs AR 0x%x, type is sys\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+ }
+
+ if ((cs_ar & AR_TYPE_MASK) >= 8 && (cs_ar & AR_TYPE_MASK) < 12 &&
+ AR_DPL(cs_ar) !=
+ (vmcs_read16(GUEST_CS_SELECTOR) & SELECTOR_RPL_MASK) ) {
+ vcpu_printf(vcpu, "%s: cs AR 0x%x, "
+ "DPL(0x%x) not as RPL(0x%x)\n",
+ __FUNCTION__,
+ cs_ar, AR_DPL(cs_ar), vmcs_read16(GUEST_CS_SELECTOR) & SELECTOR_RPL_MASK);
+ return 0;
+ }
+
+ if ((cs_ar & AR_TYPE_MASK) >= 13 && (cs_ar & AR_TYPE_MASK) < 16 &&
+ AR_DPL(cs_ar) >
+ (vmcs_read16(GUEST_CS_SELECTOR) & SELECTOR_RPL_MASK) ) {
+ vcpu_printf(vcpu, "%s: cs AR 0x%x, "
+ "DPL greater than RPL\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+ }
+
+ if (!(cs_ar & AR_P_MASK)) {
+ vcpu_printf(vcpu, "%s: CS AR 0x%x, not "
+ "present\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+ }
+
+ if ((cs_ar & AR_RESERVD_MASK)) {
+ vcpu_printf(vcpu, "%s: CS AR 0x%x, reseved"
+ " bits are set\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+ }
+
+ if (long_mode & (cs_ar & AR_L_MASK) && (cs_ar & AR_DB_MASK)) {
+ vcpu_printf(vcpu, "%s: CS AR 0x%x, DB and L are set"
+ " in long mode\n",
+ __FUNCTION__,
+ cs_ar);
+ return 0;
+
+ }
+
+ SEG_G_TEST(CS);
+
+ if (!(ss_ar & AR_UNUSABLE_MASK)) {
+ if ((ss_ar & AR_TYPE_MASK) != 3 &&
+ (ss_ar & AR_TYPE_MASK) != 7 ) {
+ vcpu_printf(vcpu, "%s: ss AR 0x%x, usable and type"
+ " is not 3 or 7\n",
+ __FUNCTION__,
+ ss_ar);
+ return 0;
+ }
+
+ if (!(ss_ar & AR_S_MASK)) {
+ vcpu_printf(vcpu, "%s: ss AR 0x%x, usable and"
+ " is sys\n",
+ __FUNCTION__,
+ ss_ar);
+ return 0;
+ }
+ if (!(ss_ar & AR_P_MASK)) {
+ vcpu_printf(vcpu, "%s: SS AR 0x%x, usable"
+ " and not present\n",
+ __FUNCTION__,
+ ss_ar);
+ return 0;
+ }
+
+ if ((ss_ar & AR_RESERVD_MASK)) {
+ vcpu_printf(vcpu, "%s: SS AR 0x%x, reseved"
+ " bits are set\n",
+ __FUNCTION__,
+ ss_ar);
+ return 0;
+ }
+
+ SEG_G_TEST(SS);
+
+ }
+
+ if (AR_DPL(ss_ar) !=
+ (vmcs_read16(GUEST_SS_SELECTOR) & SELECTOR_RPL_MASK) ) {
+ vcpu_printf(vcpu, "%s: SS AR 0x%x, "
+ "DPL not as RPL\n",
+ __FUNCTION__,
+ ss_ar);
+ return 0;
+ }
+
+ #define SEG_AR_TEST(seg) {\
+ u32 ar = vmcs_read32(GUEST_##seg##_AR_BYTES);\
+ if (!(ar & AR_UNUSABLE_MASK)) {\
+ if (!(ar & AR_TYPE_ACCESSES_MASK)) {\
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x, "\
+ "usable and not accesses\n",\
+ __FUNCTION__,\
+ ar);\
+ return 0;\
+ }\
+ if ((ar & AR_TYPE_CODE_MASK) &&\
+ !(ar & AR_TYPE_READABLE_MASK)) {\
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x, "\
+ "code and not readable\n",\
+ __FUNCTION__,\
+ ar);\
+ return 0;\
+ }\
+ if (!(ar & AR_S_MASK)) {\
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x, usable and"\
+ " is sys\n",\
+ __FUNCTION__,\
+ ar);\
+ return 0;\
+ }\
+ if ((ar & AR_TYPE_MASK) >= 0 && \
+ (ar & AR_TYPE_MASK) < 12 && \
+ AR_DPL(ar) < (vmcs_read16(GUEST_##seg##_SELECTOR) & \
+ SELECTOR_RPL_MASK) ) {\
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x, "\
+ "DPL less than RPL\n",\
+ __FUNCTION__,\
+ ar);\
+ return 0;\
+ }\
+ if (!(ar & AR_P_MASK)) {\
+ vcpu_printf(vcpu, "%s: "#seg" AR 0x%x, usable and"\
+ " not present\n",\
+ __FUNCTION__,\
+ ar);\
+ return 0;\
+ }\
+ if ((ar & AR_RESERVD_MASK)) {\
+ vcpu_printf(vcpu, "%s: "#seg" AR"\
+ " 0x%x, reseved"\
+ " bits are set\n",\
+ __FUNCTION__,\
+ ar);\
+ return 0;\
+ }\
+ SEG_G_TEST(seg)\
+ }\
+ }
+
+#undef DS
+#undef ES
+#undef FS
+#undef GS
+
+ SEG_AR_TEST(DS);
+ SEG_AR_TEST(ES);
+ SEG_AR_TEST(FS);
+ SEG_AR_TEST(GS);
+
+ // TR test
+ if (long_mode) {
+ if ((tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) {
+ vcpu_printf(vcpu, "%s: TR AR 0x%x, long"
+ " mode and not 64bit busy"
+ " tss\n",
+ __FUNCTION__,
+ tr_ar);
+ return 0;
+ }
+ } else {
+ if ((tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_32_TSS &&
+ (tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_16_TSS) {
+ vcpu_printf(vcpu, "%s: TR AR 0x%x, legacy"
+ " mode and not 16/32bit "
+ "busy tss\n",
+ __FUNCTION__,
+ tr_ar);
+ return 0;
+ }
+
+ }
+ if ((tr_ar & AR_S_MASK)) {
+ vcpu_printf(vcpu, "%s: TR AR 0x%x, S is set\n",
+ __FUNCTION__,
+ tr_ar);
+ return 0;
+ }
+ if (!(tr_ar & AR_P_MASK)) {
+ vcpu_printf(vcpu, "%s: TR AR 0x%x, P is not set\n",
+ __FUNCTION__,
+ tr_ar);
+ return 0;
+ }
+
+ if ((tr_ar & (AR_RESERVD_MASK| AR_UNUSABLE_MASK))) {
+ vcpu_printf(vcpu, "%s: TR AR 0x%x, reserved bit are"
+ " set\n",
+ __FUNCTION__,
+ tr_ar);
+ return 0;
+ }
+ SEG_G_TEST(TR);
+
+ // TR test
+ if (!(ldtr_ar & AR_UNUSABLE_MASK)) {
+
+ if ((ldtr_ar & AR_TYPE_MASK) != AR_TYPE_LDT) {
+ vcpu_printf(vcpu, "%s: LDTR AR 0x%x,"
+ " bad type\n",
+ __FUNCTION__,
+ ldtr_ar);
+ return 0;
+ }
+
+ if ((ldtr_ar & AR_S_MASK)) {
+ vcpu_printf(vcpu, "%s: LDTR AR 0x%x,"
+ " S is set\n",
+ __FUNCTION__,
+ ldtr_ar);
+ return 0;
+ }
+
+ if (!(ldtr_ar & AR_P_MASK)) {
+ vcpu_printf(vcpu, "%s: LDTR AR 0x%x,"
+ " P is not set\n",
+ __FUNCTION__,
+ ldtr_ar);
+ return 0;
+ }
+ if ((ldtr_ar & AR_RESERVD_MASK)) {
+ vcpu_printf(vcpu, "%s: LDTR AR 0x%x,"
+ " reserved bit are set\n",
+ __FUNCTION__,
+ ldtr_ar);
+ return 0;
+ }
+ SEG_G_TEST(LDTR);
+ }
+ }
+
+ // GDTR and IDTR
+
+
+ #define IDT_GDT_TEST(reg)\
+ if (!is_canonical(vmcs_readl(GUEST_##reg##_BASE))) {\
+ vcpu_printf(vcpu, "%s: "#reg" BASE 0x%lx, not canonical\n",\
+ __FUNCTION__,\
+ vmcs_readl(GUEST_##reg##_BASE));\
+ return 0;\
+ }\
+ if (vmcs_read32(GUEST_##reg##_LIMIT) >> 16) {\
+ vcpu_printf(vcpu, "%s: "#reg" LIMIT 0x%x, size err\n",\
+ __FUNCTION__,\
+ vmcs_read32(GUEST_##reg##_LIMIT));\
+ return 0;\
+ }\
+
+ IDT_GDT_TEST(GDTR);
+ IDT_GDT_TEST(IDTR);
+
+
+ // RIP
+
+ if ((!long_mode || !(vmcs_read32(GUEST_CS_AR_BYTES) & AR_L_MASK)) &&
+ vmcs_readl(GUEST_RIP) & ~((1ULL << 32) - 1) ){
+ vcpu_printf(vcpu, "%s: RIP 0x%lx, size err\n",
+ __FUNCTION__,
+ vmcs_readl(GUEST_RIP));
+ return 0;
+ }
+
+ if (!is_canonical(vmcs_readl(GUEST_RIP))) {
+ vcpu_printf(vcpu, "%s: RIP 0x%lx, not canonical\n",
+ __FUNCTION__,
+ vmcs_readl(GUEST_RIP));
+ return 0;
+ }
+
+ // RFLAGS
+ #define RFLAGS_RESEVED_CLEAR_BITS\
+ (~((1ULL << 22) - 1) | (1ULL << 15) | (1ULL << 5) | (1ULL << 3))
+ #define RFLAGS_RESEVED_SET_BITS (1 << 1)
+
+ if ((rflags & RFLAGS_RESEVED_CLEAR_BITS) ||
+ !(rflags & RFLAGS_RESEVED_SET_BITS)) {
+ vcpu_printf(vcpu, "%s: RFLAGS 0x%lx, reserved bits 0x%llx 0x%x\n",
+ __FUNCTION__,
+ rflags,
+ RFLAGS_RESEVED_CLEAR_BITS,
+ RFLAGS_RESEVED_SET_BITS);
+ return 0;
+ }
+
+ if (long_mode && virtual8086) {
+ vcpu_printf(vcpu, "%s: RFLAGS 0x%lx, vm and long mode\n",
+ __FUNCTION__,
+ rflags);
+ return 0;
+ }
+
+
+ if (!(rflags & RFLAGS_RF)) {
+ u32 vm_entry_info = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD);
+ if ((vm_entry_info & INTR_INFO_VALID_MASK) &&
+ (vm_entry_info & INTR_INFO_INTR_TYPE_MASK) ==
+ INTR_TYPE_EXT_INTR) {
+ vcpu_printf(vcpu, "%s: RFLAGS 0x%lx, external"
+ " interrupt and RF is clear\n",
+ __FUNCTION__,
+ rflags);
+ return 0;
+ }
+
+ }
+
+ // to be continued from Checks on Guest Non-Register State (22.3.1.5)
+ return 1;
+}
+
+static int check_fixed_bits(struct kvm_vcpu *vcpu, const char *reg,
+ unsigned long cr,
+ u32 msr_fixed_0, u32 msr_fixed_1)
+{
+ u64 fixed_bits_0, fixed_bits_1;
+
+ rdmsrl(msr_fixed_0, fixed_bits_0);
+ rdmsrl(msr_fixed_1, fixed_bits_1);
+ if ((cr & fixed_bits_0) != fixed_bits_0) {
+ vcpu_printf(vcpu, "%s: %s (%lx) has one of %llx unset\n",
+ __FUNCTION__, reg, cr, fixed_bits_0);
+ return 0;
+ }
+ if ((~cr & ~fixed_bits_1) != ~fixed_bits_1) {
+ vcpu_printf(vcpu, "%s: %s (%lx) has one of %llx set\n",
+ __FUNCTION__, reg, cr, ~fixed_bits_1);
+ return 0;
+ }
+ return 1;
+}
+
+static int phys_addr_width(void)
+{
+ unsigned eax, ebx, ecx, edx;
+
+ cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
+ return eax & 0xff;
+}
+
+static int check_canonical(struct kvm_vcpu *vcpu, const char *name,
+ unsigned long reg)
+{
+#ifdef CONFIG_X86_64
+ unsigned long x;
+
+ if (sizeof(reg) == 4)
+ return 1;
+ x = (long)reg >> 48;
+ if (!(x == 0 || x == ~0UL)) {
+ vcpu_printf(vcpu, "%s: %s (%lx) not canonical\n",
+ __FUNCTION__, name, reg);
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static int check_selector(struct kvm_vcpu *vcpu, const char *name,
+ int rpl_ti, int null,
+ u16 sel)
+{
+ if (rpl_ti && (sel & 7)) {
+ vcpu_printf(vcpu, "%s: %s (%x) nonzero rpl or ti\n",
+ __FUNCTION__, name, sel);
+ return 0;
+ }
+ if (null && !sel) {
+ vcpu_printf(vcpu, "%s: %s (%x) zero\n",
+ __FUNCTION__, name, sel);
+ return 0;
+ }
+ return 1;
+}
+
+#define MSR_IA32_VMX_CR0_FIXED0 0x486
+#define MSR_IA32_VMX_CR0_FIXED1 0x487
+
+#define MSR_IA32_VMX_CR4_FIXED0 0x488
+#define MSR_IA32_VMX_CR4_FIXED1 0x489
+
+int vm_entry_test_host(struct kvm_vcpu *vcpu)
+{
+ int r = 0;
+ unsigned long cr0 = vmcs_readl(HOST_CR0);
+ unsigned long cr4 = vmcs_readl(HOST_CR4);
+ unsigned long cr3 = vmcs_readl(HOST_CR3);
+ int host_64;
+
+ host_64 = vmcs_read32(VM_EXIT_CONTROLS) & VM_EXIT_HOST_ADD_SPACE_SIZE;
+
+ /* 22.2.2 */
+ r &= check_fixed_bits(vcpu, "host cr0", cr0, MSR_IA32_VMX_CR0_FIXED0,
+ MSR_IA32_VMX_CR0_FIXED1);
+
+ r &= check_fixed_bits(vcpu, "host cr0", cr4, MSR_IA32_VMX_CR4_FIXED0,
+ MSR_IA32_VMX_CR4_FIXED1);
+ if ((u64)cr3 >> phys_addr_width()) {
+ vcpu_printf(vcpu, "%s: cr3 (%lx) vs phys addr width\n",
+ __FUNCTION__, cr3);
+ r = 0;
+ }
+
+ r &= check_canonical(vcpu, "host ia32_sysenter_eip",
+ vmcs_readl(HOST_IA32_SYSENTER_EIP));
+ r &= check_canonical(vcpu, "host ia32_sysenter_esp",
+ vmcs_readl(HOST_IA32_SYSENTER_ESP));
+
+ /* 22.2.3 */
+ r &= check_selector(vcpu, "host cs", 1, 1,
+ vmcs_read16(HOST_CS_SELECTOR));
+ r &= check_selector(vcpu, "host ss", 1, !host_64,
+ vmcs_read16(HOST_SS_SELECTOR));
+ r &= check_selector(vcpu, "host ds", 1, 0,
+ vmcs_read16(HOST_DS_SELECTOR));
+ r &= check_selector(vcpu, "host es", 1, 0,
+ vmcs_read16(HOST_ES_SELECTOR));
+ r &= check_selector(vcpu, "host fs", 1, 0,
+ vmcs_read16(HOST_FS_SELECTOR));
+ r &= check_selector(vcpu, "host gs", 1, 0,
+ vmcs_read16(HOST_GS_SELECTOR));
+ r &= check_selector(vcpu, "host tr", 1, 1,
+ vmcs_read16(HOST_TR_SELECTOR));
+
+#ifdef CONFIG_X86_64
+ r &= check_canonical(vcpu, "host fs base",
+ vmcs_readl(HOST_FS_BASE));
+ r &= check_canonical(vcpu, "host gs base",
+ vmcs_readl(HOST_GS_BASE));
+ r &= check_canonical(vcpu, "host gdtr base",
+ vmcs_readl(HOST_GDTR_BASE));
+ r &= check_canonical(vcpu, "host idtr base",
+ vmcs_readl(HOST_IDTR_BASE));
+#endif
+
+ /* 22.2.4 */
+#ifdef CONFIG_X86_64
+ if (!host_64) {
+ vcpu_printf(vcpu, "%s: vm exit controls: !64 bit host\n",
+ __FUNCTION__);
+ r = 0;
+ }
+ if (!(cr4 & CR4_PAE_MASK)) {
+ vcpu_printf(vcpu, "%s: cr4 (%lx): !pae\n",
+ __FUNCTION__, cr4);
+ r = 0;
+ }
+ r &= check_canonical(vcpu, "host rip", vmcs_readl(HOST_RIP));
+#endif
+
+ return r;
+}
+
+int vm_entry_test(struct kvm_vcpu *vcpu)
+{
+ int rg, rh;
+
+ rg = vm_entry_test_guest(vcpu);
+ rh = vm_entry_test_host(vcpu);
+ return rg && rh;
+}
+
+void vmcs_dump(struct kvm_vcpu *vcpu)
+{
+ vcpu_printf(vcpu, "************************ vmcs_dump ************************\n");
+ vcpu_printf(vcpu, "VM_ENTRY_CONTROLS 0x%x\n", vmcs_read32(VM_ENTRY_CONTROLS));
+
+ vcpu_printf(vcpu, "GUEST_CR0 0x%lx\n", vmcs_readl(GUEST_CR0));
+ vcpu_printf(vcpu, "GUEST_CR3 0x%lx\n", vmcs_readl(GUEST_CR3));
+ vcpu_printf(vcpu, "GUEST_CR4 0x%lx\n", vmcs_readl(GUEST_CR4));
+
+ vcpu_printf(vcpu, "GUEST_SYSENTER_ESP 0x%lx\n", vmcs_readl(GUEST_SYSENTER_ESP));
+ vcpu_printf(vcpu, "GUEST_SYSENTER_EIP 0x%lx\n", vmcs_readl(GUEST_SYSENTER_EIP));
+
+
+ vcpu_printf(vcpu, "GUEST_IA32_DEBUGCTL 0x%llx\n", vmcs_read64(GUEST_IA32_DEBUGCTL));
+ vcpu_printf(vcpu, "GUEST_DR7 0x%lx\n", vmcs_readl(GUEST_DR7));
+
+ vcpu_printf(vcpu, "GUEST_RFLAGS 0x%lx\n", vmcs_readl(GUEST_RFLAGS));
+ vcpu_printf(vcpu, "GUEST_RIP 0x%lx\n", vmcs_readl(GUEST_RIP));
+
+ vcpu_printf(vcpu, "GUEST_CS_SELECTOR 0x%x\n", vmcs_read16(GUEST_CS_SELECTOR));
+ vcpu_printf(vcpu, "GUEST_DS_SELECTOR 0x%x\n", vmcs_read16(GUEST_DS_SELECTOR));
+ vcpu_printf(vcpu, "GUEST_ES_SELECTOR 0x%x\n", vmcs_read16(GUEST_ES_SELECTOR));
+ vcpu_printf(vcpu, "GUEST_FS_SELECTOR 0x%x\n", vmcs_read16(GUEST_FS_SELECTOR));
+ vcpu_printf(vcpu, "GUEST_GS_SELECTOR 0x%x\n", vmcs_read16(GUEST_GS_SELECTOR));
+ vcpu_printf(vcpu, "GUEST_SS_SELECTOR 0x%x\n", vmcs_read16(GUEST_SS_SELECTOR));
+
+ vcpu_printf(vcpu, "GUEST_TR_SELECTOR 0x%x\n", vmcs_read16(GUEST_TR_SELECTOR));
+ vcpu_printf(vcpu, "GUEST_LDTR_SELECTOR 0x%x\n", vmcs_read16(GUEST_LDTR_SELECTOR));
+
+ vcpu_printf(vcpu, "GUEST_CS_AR_BYTES 0x%x\n", vmcs_read32(GUEST_CS_AR_BYTES));
+ vcpu_printf(vcpu, "GUEST_DS_AR_BYTES 0x%x\n", vmcs_read32(GUEST_DS_AR_BYTES));
+ vcpu_printf(vcpu, "GUEST_ES_AR_BYTES 0x%x\n", vmcs_read32(GUEST_ES_AR_BYTES));
+ vcpu_printf(vcpu, "GUEST_FS_AR_BYTES 0x%x\n", vmcs_read32(GUEST_FS_AR_BYTES));
+ vcpu_printf(vcpu, "GUEST_GS_AR_BYTES 0x%x\n", vmcs_read32(GUEST_GS_AR_BYTES));
+ vcpu_printf(vcpu, "GUEST_SS_AR_BYTES 0x%x\n", vmcs_read32(GUEST_SS_AR_BYTES));
+
+ vcpu_printf(vcpu, "GUEST_LDTR_AR_BYTES 0x%x\n", vmcs_read32(GUEST_LDTR_AR_BYTES));
+ vcpu_printf(vcpu, "GUEST_TR_AR_BYTES 0x%x\n", vmcs_read32(GUEST_TR_AR_BYTES));
+
+ vcpu_printf(vcpu, "GUEST_CS_BASE 0x%lx\n", vmcs_readl(GUEST_CS_BASE));
+ vcpu_printf(vcpu, "GUEST_DS_BASE 0x%lx\n", vmcs_readl(GUEST_DS_BASE));
+ vcpu_printf(vcpu, "GUEST_ES_BASE 0x%lx\n", vmcs_readl(GUEST_ES_BASE));
+ vcpu_printf(vcpu, "GUEST_FS_BASE 0x%lx\n", vmcs_readl(GUEST_FS_BASE));
+ vcpu_printf(vcpu, "GUEST_GS_BASE 0x%lx\n", vmcs_readl(GUEST_GS_BASE));
+ vcpu_printf(vcpu, "GUEST_SS_BASE 0x%lx\n", vmcs_readl(GUEST_SS_BASE));
+
+
+ vcpu_printf(vcpu, "GUEST_LDTR_BASE 0x%lx\n", vmcs_readl(GUEST_LDTR_BASE));
+ vcpu_printf(vcpu, "GUEST_TR_BASE 0x%lx\n", vmcs_readl(GUEST_TR_BASE));
+
+ vcpu_printf(vcpu, "GUEST_CS_LIMIT 0x%x\n", vmcs_read32(GUEST_CS_LIMIT));
+ vcpu_printf(vcpu, "GUEST_DS_LIMIT 0x%x\n", vmcs_read32(GUEST_DS_LIMIT));
+ vcpu_printf(vcpu, "GUEST_ES_LIMIT 0x%x\n", vmcs_read32(GUEST_ES_LIMIT));
+ vcpu_printf(vcpu, "GUEST_FS_LIMIT 0x%x\n", vmcs_read32(GUEST_FS_LIMIT));
+ vcpu_printf(vcpu, "GUEST_GS_LIMIT 0x%x\n", vmcs_read32(GUEST_GS_LIMIT));
+ vcpu_printf(vcpu, "GUEST_SS_LIMIT 0x%x\n", vmcs_read32(GUEST_SS_LIMIT));
+
+ vcpu_printf(vcpu, "GUEST_LDTR_LIMIT 0x%x\n", vmcs_read32(GUEST_LDTR_LIMIT));
+ vcpu_printf(vcpu, "GUEST_TR_LIMIT 0x%x\n", vmcs_read32(GUEST_TR_LIMIT));
+
+ vcpu_printf(vcpu, "GUEST_GDTR_BASE 0x%lx\n", vmcs_readl(GUEST_GDTR_BASE));
+ vcpu_printf(vcpu, "GUEST_IDTR_BASE 0x%lx\n", vmcs_readl(GUEST_IDTR_BASE));
+
+ vcpu_printf(vcpu, "GUEST_GDTR_LIMIT 0x%x\n", vmcs_read32(GUEST_GDTR_LIMIT));
+ vcpu_printf(vcpu, "GUEST_IDTR_LIMIT 0x%x\n", vmcs_read32(GUEST_IDTR_LIMIT));
+
+ vcpu_printf(vcpu, "EXCEPTION_BITMAP 0x%x\n", vmcs_read32(EXCEPTION_BITMAP));
+ vcpu_printf(vcpu, "***********************************************************\n");
+}
+
+void regs_dump(struct kvm_vcpu *vcpu)
+{
+ #define REG_DUMP(reg) \
+ vcpu_printf(vcpu, #reg" = 0x%lx(VCPU)\n", vcpu->regs[VCPU_REGS_##reg])
+ #define VMCS_REG_DUMP(reg) \
+ vcpu_printf(vcpu, #reg" = 0x%lx(VMCS)\n", vmcs_readl(GUEST_##reg))
+
+ vcpu_printf(vcpu, "************************ regs_dump ************************\n");
+ REG_DUMP(RAX);
+ REG_DUMP(RBX);
+ REG_DUMP(RCX);
+ REG_DUMP(RDX);
+ REG_DUMP(RSP);
+ REG_DUMP(RBP);
+ REG_DUMP(RSI);
+ REG_DUMP(RDI);
+ REG_DUMP(R8);
+ REG_DUMP(R9);
+ REG_DUMP(R10);
+ REG_DUMP(R11);
+ REG_DUMP(R12);
+ REG_DUMP(R13);
+ REG_DUMP(R14);
+ REG_DUMP(R15);
+
+ VMCS_REG_DUMP(RSP);
+ VMCS_REG_DUMP(RIP);
+ VMCS_REG_DUMP(RFLAGS);
+
+ vcpu_printf(vcpu, "***********************************************************\n");
+}
+
+void sregs_dump(struct kvm_vcpu *vcpu)
+{
+ vcpu_printf(vcpu, "************************ sregs_dump ************************\n");
+ vcpu_printf(vcpu, "cr0 = 0x%lx\n", vcpu->cr0);
+ vcpu_printf(vcpu, "cr2 = 0x%lx\n", vcpu->cr2);
+ vcpu_printf(vcpu, "cr3 = 0x%lx\n", vcpu->cr3);
+ vcpu_printf(vcpu, "cr4 = 0x%lx\n", vcpu->cr4);
+ vcpu_printf(vcpu, "cr8 = 0x%lx\n", vcpu->cr8);
+ vcpu_printf(vcpu, "shadow_efer = 0x%llx\n", vcpu->shadow_efer);
+ vcpu_printf(vcpu, "***********************************************************\n");
+}
+
+void show_pending_interrupts(struct kvm_vcpu *vcpu)
+{
+ int i;
+ vcpu_printf(vcpu, "************************ pending interrupts ****************\n");
+ vcpu_printf(vcpu, "sumamry = 0x%lx\n", vcpu->irq_summary);
+ for (i=0 ; i < NR_IRQ_WORDS ; i++)
+ vcpu_printf(vcpu, "%lx ", vcpu->irq_pending[i]);
+ vcpu_printf(vcpu, "\n");
+ vcpu_printf(vcpu, "************************************************************\n");
+}
+
+void vcpu_dump(struct kvm_vcpu *vcpu)
+{
+ regs_dump(vcpu);
+ sregs_dump(vcpu);
+ vmcs_dump(vcpu);
+ show_msrs(vcpu);
+ show_pending_interrupts(vcpu);
+ /* more ... */
+}
+#endif
+
diff --git a/kvm/kernel/x86_64.config b/kvm/kernel/x86_64.config
new file mode 100644
index 000000000..f94a5dd6d
--- /dev/null
+++ b/kvm/kernel/x86_64.config
@@ -0,0 +1,1711 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.20
+# Sun Feb 18 18:16:05 2007
+#
+CONFIG_X86_64=y
+CONFIG_64BIT=y
+CONFIG_X86=y
+CONFIG_ZONE_DMA32=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_SEMAPHORE_SLEEPERS=y
+CONFIG_MMU=y
+CONFIG_ZONE_DMA=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_DMI=y
+CONFIG_AUDIT_ARCH=y
+CONFIG_GENERIC_BUG=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_IPC_NS is not set
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_UTS_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+# CONFIG_CPUSETS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_RELAY=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SHMEM=y
+CONFIG_SLAB=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+# CONFIG_SLOB is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+
+#
+# Block layer
+#
+CONFIG_BLOCK=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+
+#
+# Processor type and features
+#
+CONFIG_X86_PC=y
+# CONFIG_X86_VSMP is not set
+# CONFIG_MK8 is not set
+# CONFIG_MPSC is not set
+# CONFIG_MCORE2 is not set
+CONFIG_GENERIC_CPU=y
+CONFIG_X86_L1_CACHE_BYTES=128
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_X86_INTERNODE_CACHE_BYTES=128
+CONFIG_X86_TSC=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_MICROCODE=m
+CONFIG_MICROCODE_OLD_INTERFACE=y
+CONFIG_X86_MSR=m
+CONFIG_X86_CPUID=m
+CONFIG_X86_HT=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_MTRR=y
+CONFIG_SMP=y
+# CONFIG_SCHED_SMT is not set
+CONFIG_SCHED_MC=y
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_BKL=y
+# CONFIG_NUMA is not set
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_RESOURCES_64BIT=y
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_NR_CPUS=8
+CONFIG_HOTPLUG_CPU=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_HPET_TIMER=y
+CONFIG_HPET_EMULATE_RTC=y
+CONFIG_IOMMU=y
+# CONFIG_CALGARY_IOMMU is not set
+CONFIG_SWIOTLB=y
+CONFIG_X86_MCE=y
+CONFIG_X86_MCE_INTEL=y
+CONFIG_X86_MCE_AMD=y
+CONFIG_KEXEC=y
+# CONFIG_CRASH_DUMP is not set
+CONFIG_PHYSICAL_START=0x200000
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_REORDER is not set
+CONFIG_K8_NB=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_ISA_DMA_API=y
+CONFIG_GENERIC_PENDING_IRQ=y
+
+#
+# Power management options
+#
+CONFIG_PM=y
+CONFIG_PM_LEGACY=y
+CONFIG_PM_DEBUG=y
+# CONFIG_DISABLE_CONSOLE_SUSPEND is not set
+# CONFIG_PM_SYSFS_DEPRECATED is not set
+CONFIG_SOFTWARE_SUSPEND=y
+CONFIG_PM_STD_PARTITION=""
+CONFIG_SUSPEND_SMP=y
+
+#
+# ACPI (Advanced Configuration and Power Interface) Support
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_SLEEP_PROC_FS=y
+# CONFIG_ACPI_SLEEP_PROC_SLEEP is not set
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_AC=m
+CONFIG_ACPI_BATTERY=m
+CONFIG_ACPI_BUTTON=m
+CONFIG_ACPI_VIDEO=m
+# CONFIG_ACPI_HOTKEY is not set
+CONFIG_ACPI_FAN=y
+# CONFIG_ACPI_DOCK is not set
+# CONFIG_ACPI_BAY is not set
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_HOTPLUG_CPU=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_ASUS=m
+CONFIG_ACPI_IBM=m
+CONFIG_ACPI_IBM_DOCK=y
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_BLACKLIST_YEAR=0
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_X86_PM_TIMER=y
+CONFIG_ACPI_CONTAINER=y
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+
+#
+# CPUFreq processor drivers
+#
+CONFIG_X86_POWERNOW_K8=y
+CONFIG_X86_POWERNOW_K8_ACPI=y
+CONFIG_X86_SPEEDSTEP_CENTRINO=y
+CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y
+CONFIG_X86_ACPI_CPUFREQ=m
+
+#
+# shared options
+#
+# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
+# CONFIG_X86_SPEEDSTEP_LIB is not set
+
+#
+# Bus options (PCI etc.)
+#
+CONFIG_PCI=y
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIEAER=y
+CONFIG_PCI_MSI=y
+# CONFIG_PCI_DEBUG is not set
+CONFIG_HT_IRQ=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats / Emulations
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_IA32_EMULATION=y
+# CONFIG_IA32_AOUT is not set
+CONFIG_COMPAT=y
+CONFIG_SYSVIPC_COMPAT=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_NETDEBUG is not set
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+CONFIG_NET_KEY=m
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+# CONFIG_IP_ROUTE_MULTIPATH_CACHED is not set
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_TUNNEL=m
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+
+#
+# IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS transport protocol load balancing support
+#
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+
+#
+# IPVS scheduler
+#
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETLABEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NETFILTER_NETLINK_QUEUE=m
+CONFIG_NETFILTER_NETLINK_LOG=m
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+CONFIG_NETFILTER_XTABLES=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_ULOG=m
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+# CONFIG_DECNET is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_NET_SCH_CLK_JIFFIES is not set
+CONFIG_NET_SCH_CLK_GETTIMEOFDAY=y
+# CONFIG_NET_SCH_CLK_CPU is not set
+
+#
+# Queueing/Scheduling
+#
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_INGRESS=m
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_CLS_POLICE=y
+CONFIG_NET_CLS_IND=y
+CONFIG_NET_ESTIMATOR=y
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP is not set
+# CONFIG_IEEE80211_SOFTMAC is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_FIB_RULES=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+CONFIG_CONNECTOR=m
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG is not set
+
+#
+# Protocols
+#
+CONFIG_PNPACPI=y
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# Misc devices
+#
+# CONFIG_IBM_ASM is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_MSI_LAPTOP is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_BLK_DEV_IDEFLOPPY=y
+CONFIG_BLK_DEV_IDESCSI=m
+# CONFIG_BLK_DEV_IDEACPI is not set
+CONFIG_IDE_TASK_IOCTL=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+CONFIG_BLK_DEV_CMD640=y
+CONFIG_BLK_DEV_CMD640_ENHANCED=y
+CONFIG_BLK_DEV_IDEPNP=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_AEC62XX=y
+CONFIG_BLK_DEV_ALI15X3=y
+# CONFIG_WDC_ALI15X3 is not set
+CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_BLK_DEV_ATIIXP=y
+CONFIG_BLK_DEV_CMD64X=y
+CONFIG_BLK_DEV_TRIFLEX=y
+CONFIG_BLK_DEV_CY82C693=y
+CONFIG_BLK_DEV_CS5520=y
+CONFIG_BLK_DEV_CS5530=y
+CONFIG_BLK_DEV_HPT34X=y
+# CONFIG_HPT34X_AUTODMA is not set
+CONFIG_BLK_DEV_HPT366=y
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_IT8213 is not set
+CONFIG_BLK_DEV_IT821X=y
+# CONFIG_BLK_DEV_NS87415 is not set
+CONFIG_BLK_DEV_PDC202XX_OLD=y
+# CONFIG_PDC202XX_BURST is not set
+CONFIG_BLK_DEV_PDC202XX_NEW=y
+CONFIG_BLK_DEV_SVWKS=y
+CONFIG_BLK_DEV_SIIMAGE=y
+CONFIG_BLK_DEV_SIS5513=y
+CONFIG_BLK_DEV_SLC90E66=y
+# CONFIG_BLK_DEV_TRM290 is not set
+CONFIG_BLK_DEV_VIA82CXXX=y
+# CONFIG_BLK_DEV_TC86C001 is not set
+# CONFIG_IDE_ARM is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_IVB is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+CONFIG_RAID_ATTRS=m
+CONFIG_SCSI=m
+# CONFIG_SCSI_TGT is not set
+CONFIG_SCSI_NETLINK=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+CONFIG_CHR_DEV_SCH=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+# CONFIG_SCSI_SCAN_ASYNC is not set
+
+#
+# SCSI Transports
+#
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_ISCSI_ATTRS=m
+CONFIG_SCSI_SAS_ATTRS=m
+# CONFIG_SCSI_SAS_LIBSAS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_SRP is not set
+
+#
+# Serial ATA (prod) and Parallel ATA (experimental) drivers
+#
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+CONFIG_SATA_AHCI=m
+# CONFIG_SATA_SVW is not set
+CONFIG_ATA_PIIX=m
+# CONFIG_SATA_MV is not set
+# CONFIG_SATA_NV is not set
+# CONFIG_PDC_ADMA is not set
+# CONFIG_SATA_QSTOR is not set
+# CONFIG_SATA_PROMISE is not set
+# CONFIG_SATA_SX4 is not set
+# CONFIG_SATA_SIL is not set
+# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIS is not set
+# CONFIG_SATA_ULI is not set
+# CONFIG_SATA_VIA is not set
+# CONFIG_SATA_VITESSE is not set
+# CONFIG_SATA_INIC162X is not set
+CONFIG_SATA_INTEL_COMBINED=y
+# CONFIG_PATA_ALI is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ARTOP is not set
+# CONFIG_PATA_ATIIXP is not set
+# CONFIG_PATA_CMD64X is not set
+# CONFIG_PATA_CS5520 is not set
+# CONFIG_PATA_CS5530 is not set
+# CONFIG_PATA_CYPRESS is not set
+# CONFIG_PATA_EFAR is not set
+# CONFIG_ATA_GENERIC is not set
+# CONFIG_PATA_HPT366 is not set
+# CONFIG_PATA_HPT37X is not set
+# CONFIG_PATA_HPT3X2N is not set
+# CONFIG_PATA_HPT3X3 is not set
+# CONFIG_PATA_IT821X is not set
+# CONFIG_PATA_IT8213 is not set
+# CONFIG_PATA_JMICRON is not set
+# CONFIG_PATA_TRIFLEX is not set
+# CONFIG_PATA_MARVELL is not set
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PDC_OLD is not set
+# CONFIG_PATA_RADISYS is not set
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+# CONFIG_PATA_SERVERWORKS is not set
+# CONFIG_PATA_PDC2027X is not set
+# CONFIG_PATA_SIL680 is not set
+# CONFIG_PATA_SIS is not set
+# CONFIG_PATA_VIA is not set
+# CONFIG_PATA_WINBOND is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+# CONFIG_MD_RAID456 is not set
+CONFIG_MD_MULTIPATH=m
+CONFIG_MD_FAULTY=m
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_SPI is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+# CONFIG_MAC_EMUMOUSEBTN is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+CONFIG_BONDING=m
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_NET_SB1000 is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# PHY device support
+#
+CONFIG_PHYLIB=m
+
+#
+# MII PHY device drivers
+#
+CONFIG_MARVELL_PHY=m
+CONFIG_DAVICOM_PHY=m
+CONFIG_QSEMI_PHY=m
+CONFIG_LXT_PHY=m
+CONFIG_CICADA_PHY=m
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_FIXED_PHY is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_HAPPYMEAL=m
+CONFIG_SUNGEM=m
+CONFIG_CASSINI=m
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+CONFIG_DE2104X=m
+CONFIG_TULIP=m
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+# CONFIG_TULIP_NAPI is not set
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+CONFIG_DM9102=m
+CONFIG_ULI526X=m
+CONFIG_HP100=m
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+# CONFIG_PCNET32_NAPI is not set
+CONFIG_AMD8111_ETH=m
+CONFIG_AMD8111E_NAPI=y
+CONFIG_ADAPTEC_STARFIRE=m
+CONFIG_ADAPTEC_STARFIRE_NAPI=y
+CONFIG_B44=m
+CONFIG_FORCEDETH=m
+# CONFIG_FORCEDETH_NAPI is not set
+CONFIG_DGRS=m
+# CONFIG_EEPRO100 is not set
+CONFIG_E100=m
+CONFIG_FEALNX=m
+CONFIG_NATSEMI=m
+CONFIG_NE2K_PCI=m
+CONFIG_8139CP=m
+CONFIG_8139TOO=m
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+CONFIG_8139TOO_8129=y
+# CONFIG_8139_OLD_RX_RESET is not set
+CONFIG_SIS900=m
+CONFIG_EPIC100=m
+CONFIG_SUNDANCE=m
+# CONFIG_SUNDANCE_MMIO is not set
+CONFIG_VIA_RHINE=m
+CONFIG_VIA_RHINE_MMIO=y
+# CONFIG_VIA_RHINE_NAPI is not set
+# CONFIG_SC92031 is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+CONFIG_ACENIC=m
+# CONFIG_ACENIC_OMIT_TIGON_I is not set
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000_NAPI=y
+# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+CONFIG_NS83820=m
+CONFIG_HAMACHI=m
+CONFIG_YELLOWFIN=m
+CONFIG_R8169=m
+CONFIG_R8169_NAPI=y
+CONFIG_R8169_VLAN=y
+CONFIG_SIS190=m
+CONFIG_SKGE=m
+CONFIG_SKY2=m
+# CONFIG_SK98LIN is not set
+CONFIG_VIA_VELOCITY=m
+CONFIG_TIGON3=m
+CONFIG_BNX2=m
+# CONFIG_QLA3XXX is not set
+# CONFIG_ATL1 is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+CONFIG_CHELSIO_T1=m
+# CONFIG_CHELSIO_T1_1G is not set
+CONFIG_CHELSIO_T1_NAPI=y
+# CONFIG_CHELSIO_T3 is not set
+CONFIG_IXGB=m
+CONFIG_IXGB_NAPI=y
+CONFIG_S2IO=m
+CONFIG_S2IO_NAPI=y
+# CONFIG_MYRI10GE is not set
+# CONFIG_NETXEN_NIC is not set
+
+#
+# Token Ring devices
+#
+CONFIG_TR=y
+CONFIG_IBMOL=m
+CONFIG_3C359=m
+# CONFIG_TMS380TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+CONFIG_NET_WIRELESS_RTNETLINK=y
+
+#
+# Obsolete Wireless cards support (pre-802.11)
+#
+# CONFIG_STRIP is not set
+
+#
+# Wireless 802.11b ISA/PCI cards support
+#
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+# CONFIG_IPW2100_DEBUG is not set
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+# CONFIG_IPW2200_RADIOTAP is not set
+# CONFIG_IPW2200_PROMISCUOUS is not set
+# CONFIG_IPW2200_QOS is not set
+# CONFIG_IPW2200_DEBUG is not set
+CONFIG_AIRO=m
+CONFIG_HERMES=m
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_NORTEL_HERMES=m
+CONFIG_PCI_HERMES=m
+CONFIG_ATMEL=m
+CONFIG_PCI_ATMEL=m
+
+#
+# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
+#
+CONFIG_PRISM54=m
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+CONFIG_HOSTAP_FIRMWARE_NVRAM=y
+CONFIG_HOSTAP_PLX=m
+CONFIG_HOSTAP_PCI=m
+CONFIG_NET_WIRELESS=y
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+CONFIG_NETCONSOLE=m
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+CONFIG_NETPOLL_TRAP=y
+CONFIG_NET_POLL_CONTROLLER=y
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_LIBPS2=y
+CONFIG_SERIO_RAW=m
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_MOXA_SMARTIO_NEW is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_SYNCLINK_GT is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_PNP=y
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+
+#
+# IPMI
+#
+CONFIG_IPMI_HANDLER=m
+# CONFIG_IPMI_PANIC_EVENT is not set
+CONFIG_IPMI_DEVICE_INTERFACE=m
+CONFIG_IPMI_SI=m
+CONFIG_IPMI_WATCHDOG=m
+CONFIG_IPMI_POWEROFF=m
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+CONFIG_SOFT_WATCHDOG=m
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+CONFIG_ALIM1535_WDT=m
+CONFIG_ALIM7101_WDT=m
+# CONFIG_SC520_WDT is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+CONFIG_IBMASR=m
+# CONFIG_WAFER_WDT is not set
+CONFIG_I6300ESB_WDT=m
+CONFIG_I8XX_TCO=m
+# CONFIG_ITCO_WDT is not set
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_60XX_WDT is not set
+# CONFIG_SBC8360_WDT is not set
+# CONFIG_CPU5_WDT is not set
+# CONFIG_SMSC37B787_WDT is not set
+CONFIG_W83627HF_WDT=m
+# CONFIG_W83697HF_WDT is not set
+CONFIG_W83877F_WDT=m
+CONFIG_W83977F_WDT=m
+CONFIG_MACHZ_WDT=m
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+
+#
+# PCI-based Watchdog Cards
+#
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_WDTPCI=m
+CONFIG_WDT_501_PCI=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_INTEL=y
+CONFIG_HW_RANDOM_AMD=y
+CONFIG_HW_RANDOM_GEODE=y
+CONFIG_NVRAM=y
+CONFIG_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_AGP_SIS=y
+CONFIG_AGP_VIA=y
+# CONFIG_DRM is not set
+# CONFIG_MWAVE is not set
+# CONFIG_PC8736x_GPIO is not set
+# CONFIG_RAW_DRIVER is not set
+CONFIG_HPET=y
+# CONFIG_HPET_RTC_IRQ is not set
+# CONFIG_HPET_MMAP is not set
+CONFIG_HANGCHECK_TIMER=m
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+# CONFIG_HWMON is not set
+# CONFIG_HWMON_VID is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
+CONFIG_VIDEO_SELECT=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_DEVICE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# HID Devices
+#
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+# CONFIG_USB is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# LED devices
+#
+# CONFIG_NEW_LEDS is not set
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# EDAC - error detection and reporting (RAS) (EXPERIMENTAL)
+#
+CONFIG_EDAC=y
+
+#
+# Reporting subsystems
+#
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_E752X=m
+CONFIG_EDAC_POLL=y
+
+#
+# Real Time Clock
+#
+CONFIG_RTC_LIB=m
+CONFIG_RTC_CLASS=m
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=m
+CONFIG_RTC_INTF_PROC=m
+CONFIG_RTC_INTF_DEV=m
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+
+#
+# RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_TEST is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# DMA Engine support
+#
+# CONFIG_DMA_ENGINE is not set
+
+#
+# DMA Clients
+#
+
+#
+# DMA Devices
+#
+
+#
+# Auxiliary Display support
+#
+
+#
+# Virtualization
+#
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+
+#
+# Firmware Drivers
+#
+CONFIG_EDD=m
+CONFIG_DELL_RBU=m
+CONFIG_DCDBAS=m
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+CONFIG_EXT3_FS=m
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=m
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=m
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_RAMFS=y
+CONFIG_CONFIGFS_FS=m
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_DIRECTIO=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_RPCSEC_GSS_SPKM3=m
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=m
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+CONFIG_NLS_CODEPAGE_862=m
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+CONFIG_NLS_ISO8859_8=m
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=m
+
+#
+# Distributed Lock Manager
+#
+# CONFIG_DLM is not set
+
+#
+# Instrumentation Support
+#
+# CONFIG_PROFILING is not set
+CONFIG_KPROBES=y
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_DETECT_SOFTLOCKUP=y
+CONFIG_SCHEDSTATS=y
+CONFIG_DEBUG_SLAB=y
+CONFIG_DEBUG_SLAB_LEAK=y
+CONFIG_DEBUG_PREEMPT=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_LOCKDEP=y
+# CONFIG_DEBUG_LOCKDEP is not set
+CONFIG_TRACE_IRQFLAGS=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_STACKTRACE=y
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_LKDTM is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_DEBUG_RODATA=y
+# CONFIG_IOMMU_DEBUG is not set
+CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_DEBUG_STACK_USAGE=y
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_NETWORK_XFRM=y
+CONFIG_SECURITY_CAPABILITIES=y
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_TGR192=m
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+# CONFIG_CRYPTO_TWOFISH_X86_64 is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_AES_X86_64=y
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC16=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_TEXTSEARCH=y
+CONFIG_TEXTSEARCH_KMP=m
+CONFIG_TEXTSEARCH_BM=m
+CONFIG_TEXTSEARCH_FSM=m
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
diff --git a/kvm/kvm b/kvm/kvm
new file mode 100755
index 000000000..0374fbc17
--- /dev/null
+++ b/kvm/kvm
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+
+import sys, os, time, re
+import optparse, commands
+import ConfigParser, StringIO
+
+class ShellConfigParser(ConfigParser.ConfigParser):
+ def read(self, filename):
+ try:
+ text = open(filename).read()
+ except IOError:
+ pass
+ else:
+ file = StringIO.StringIO("[shell]\n" + text)
+ self.readfp(file, filename)
+
+config = ShellConfigParser()
+config.read('config.mak')
+
+external_module = config.get('shell', 'want_module')
+privileged = os.getuid() == 0
+
+optparser = optparse.OptionParser()
+
+optparser.add_option('--no-reload-module',
+ help = 'do not reload kvm module',
+ action = 'store_false',
+ dest = 'reload',
+ default = privileged,
+ )
+
+optparser.add_option('--install',
+ help = 'start up guest in installer boot cd',
+ action = 'store_true',
+ default = False,
+ )
+
+optparser.add_option('-m', '--memory',
+ help = 'guest memory in MB',
+ type = 'int',
+ default = 384,
+ dest = 'memory',
+ )
+
+optparser.add_option('--debugger',
+ help = 'wait for gdb',
+ action = 'store_true',
+ default = False,
+ )
+
+optparser.add_option('--no-tap',
+ help = 'run the guest without tap netif',
+ action = 'store_true',
+ dest = 'notap',
+ default = not privileged,
+ )
+
+optparser.add_option('--mac',
+ help = 'use this specific mac addr',
+ dest = 'mac',
+ default = None,
+ )
+
+optparser.add_option('--vnc',
+ help = 'use VNC rather than SDL',
+ dest = 'vnc',
+ default = None,
+ )
+
+optparser.add_option('--no-kvm',
+ help = 'use standard qemu, without kvm',
+ action = 'store_false',
+ dest = 'kvm',
+ default = True,
+ )
+optparser.add_option('--image',
+ help = 'select disk image',
+ dest = 'image',
+ default = '/tmp/disk',
+ )
+optparser.add_option('--cdrom',
+ help = 'select cdrom image',
+ dest = 'cdrom',
+ default = None,
+ )
+
+optparser.add_option('--hdb',
+ help = 'secondary hard disk image',
+ dest = 'hdb',
+ default = None,
+ )
+
+optparser.add_option('--loadvm',
+ help = 'select saved vm-image',
+ dest = 'saved_image',
+ default = None,
+ )
+
+optparser.add_option('--monitor',
+ help = 'redirect monitor (currently only stdio or tcp)',
+ dest = 'monitor',
+ default = None,
+ )
+
+optparser.add_option('--stopped',
+ help = 'start image in stopped mode',
+ action = 'store_true',
+ default = False,
+ )
+
+optparser.add_option('-s', '--smp',
+ type = 'int',
+ default = 1,
+ dest = 'vcpus',
+ help = 'define number of vcpus',
+ )
+
+optparser.add_option('--no-kvm-irqchip',
+ action = 'store_false',
+ default = True,
+ dest = 'irqchip',
+ help = 'avoid using in-kernel irqchip',
+ )
+
+optparser.add_option('-n', '--dry-run',
+ help = "just print the qemu command line; don't run it",
+ action = 'store_true',
+ dest = 'dry_run',
+ default = False,
+ )
+
+
+(options, args) = optparser.parse_args()
+
+if len(args) > 0:
+ options.image = args[0]
+
+if len(args) > 1:
+ options.cdrom = args[1]
+
+def remove_module(module):
+ module = module.replace('-', '_')
+ lines = commands.getoutput('/sbin/lsmod').split('\n')
+ for x in lines:
+ if x.startswith(module + ' '):
+ if os.spawnl(os.P_WAIT, '/sbin/rmmod', 'rmmod', module) != 0:
+ raise Exception('failed to remove %s module' % (module,))
+
+def insert_module(module):
+ if os.spawnl(os.P_WAIT, '/sbin/insmod', 'insmod',
+ 'kernel/%s.ko' % (module,)) != 0:
+ raise Exception('failed to load kvm module')
+
+def probe_module(module):
+ if os.spawnl(os.P_WAIT, '/sbin/modprobe', 'modprobe', module) != 0:
+ raise Exception('failed to load kvm module')
+
+def vendor():
+ for x in file('/proc/cpuinfo').readlines():
+ m = re.match(r'vendor_id[ \t]*: *([a-zA-Z]+),*', x)
+ if m:
+ return m.group(1)
+ return unknown
+
+vendor_module = {
+ 'GenuineIntel': 'kvm-intel',
+ 'AuthenticAMD': 'kvm-amd',
+ }[vendor()]
+
+if options.kvm and options.reload:
+ for module in [vendor_module, 'kvm']:
+ remove_module(module)
+ if external_module:
+ insmod = insert_module
+ else:
+ insmod = probe_module
+ for module in ['kvm', vendor_module]:
+ insmod(module)
+ commands.getstatusoutput('/sbin/udevsettle')
+ if not os.access('/dev/kvm', os.F_OK):
+ print '/dev/kvm not present'
+
+disk = options.image
+if options.install:
+ (status, output) = commands.getstatusoutput(
+ 'qemu/qemu-img create -f qcow2 "%s" 30G' % disk)
+ if status:
+ raise Exception, output
+
+bootdisk = 'c'
+if options.install:
+ bootdisk = 'd'
+
+arch = 'x86_64'
+
+if arch == 'x86_64':
+ cmd = 'qemu-system-' + arch
+else:
+ cmd = 'qemu'
+
+local_cmd = 'qemu/' + arch + '-softmmu/' + cmd
+if os.access(local_cmd, os.F_OK):
+ cmd = local_cmd
+else:
+ cmd = '/usr/bin/kvm'
+
+qemu_args = (cmd, '-boot', bootdisk,
+ '-hda', disk, '-m', str(options.memory),
+ '-serial', 'file:/tmp/serial.log',
+ '-smp', str(options.vcpus),
+ #'-usbdevice', 'tablet',
+ )
+
+if options.cdrom:
+ qemu_args += ('-cdrom', options.cdrom,)
+
+if options.hdb:
+ qemu_args += ('-hdb', options.hdb,)
+
+if not options.kvm:
+ qemu_args += ('-no-kvm',)
+
+if options.debugger:
+ qemu_args += ('-s',)
+
+if not options.irqchip:
+ qemu_args += ('-no-kvm-irqchip',)
+
+if not options.notap:
+ mac = options.mac
+ if not mac:
+ for line in commands.getoutput('/sbin/ip link show eth0').splitlines():
+ m = re.match(r'.*link/ether (..:..:..:..:..:..).*', line)
+ if m:
+ mac = m.group(1)
+ if not mac:
+ raise Exception, 'Unable to determine eth0 mac address'
+ mac_components = mac.split(':')
+ mac_components[0] = 'a0'
+ mac = ':'.join(mac_components)
+
+ qemu_args += ('-net', 'nic,macaddr=%s,model=rtl8139' % (mac,),
+ '-net', 'tap,script=/etc/kvm/qemu-ifup',)
+
+if options.vnc:
+ qemu_args += ('-vnc', str(options.vnc))
+
+if options.saved_image:
+ qemu_args += ('-loadvm' , options.saved_image, )
+
+if options.monitor:
+ if options.monitor == 'stdio':
+ qemu_args += ('-monitor' , 'stdio', )
+ elif options.monitor == 'tcp':
+ qemu_args += ('-monitor' , 'tcp:0:5555,server,nowait', )
+ else:
+ raise Exception('illegal monitor option %s' % option.monitor)
+
+if options.stopped:
+ qemu_args += ('-S',)
+
+if options.dry_run:
+ def concat_func(x,y): return x + ' ' + y
+ print reduce(concat_func, qemu_args)
+ sys.exit(0)
+
+os.execvp(cmd, qemu_args)
diff --git a/kvm/kvm.spec b/kvm/kvm.spec
new file mode 100644
index 000000000..1cabaa391
--- /dev/null
+++ b/kvm/kvm.spec
@@ -0,0 +1,134 @@
+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
+
+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
+%endif
+
+%description
+The Kernel Virtual Machine provides a virtualization enviroment for processors
+with hardware support for virtualization: Intel's VT 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
+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
+#(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..07773b02c
--- /dev/null
+++ b/kvm/kvm_stat
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+
+import curses
+import sys, os, time, optparse
+
+class Stats:
+ def __init__(self):
+ self.base = '/sys/kernel/debug/kvm'
+ self.values = {}
+ for key in os.listdir(self.base):
+ 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)
+
+stats = Stats()
+
+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])
+
+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, args) = options.parse_args(sys.argv)
+
+if not options.once:
+ import curses.wrapper
+ curses.wrapper(tui, stats)
+else:
+ batch(stats)
diff --git a/kvm/libkvm/Makefile b/kvm/libkvm/Makefile
new file mode 100644
index 000000000..65efb3a24
--- /dev/null
+++ b/kvm/libkvm/Makefile
@@ -0,0 +1,38 @@
+include ../config.mak
+include ../user/config.mak
+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 += $(autodepend-flags) -g -fomit-frame-pointer -Wall
+CFLAGS += $(call cc-option, -fno-stack-protector, "")
+CFLAGS += $(call cc-option, -fno-stack-protector-all, "")
+CFLAGS += -I $(KERNELDIR)/include
+
+LDFLAGS += $(CFLAGS)
+
+CXXFLAGS = $(autodepend-flags)
+
+autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
+
+
+all: libkvm.a
+
+libkvm.a: libkvm.o $(libkvm-$(ARCH)-objs)
+ $(AR) rcs $@ $^
+
+install:
+ install -D libkvm.h $(DESTDIR)/$(PREFIX)/include/libkvm.h
+ install -D $(KERNELDIR)/include/linux/kvm.h \
+ $(DESTDIR)/$(PREFIX)/include/linux/kvm.h
+ install -D $(KERNELDIR)/include/linux/kvm_para.h \
+ $(DESTDIR)/$(PREFIX)/include/linux/kvm_para.h
+ install -D libkvm.a $(DESTDIR)/$(PREFIX)/$(LIBDIR)/libkvm.a
+
+-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-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..f4040be66
--- /dev/null
+++ b/kvm/libkvm/kvm-common.h
@@ -0,0 +1,86 @@
+/*
+ * 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 */
+#define KVM_MAX_NUM_MEM_REGIONS 8u
+#define MAX_VCPUS 4
+
+/* 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;
+ /// A pointer to the memory used as the physical memory for the guest
+ void *physical_memory;
+ /// 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;
+};
+
+void init_slots(void);
+int get_free_slot(kvm_context_t kvm);
+void register_slot(int slot, unsigned long phys_addr, unsigned long len,
+ int user_alloc, unsigned long userspace_addr, unsigned flags);
+
+void free_slot(int slot);
+int get_slot(unsigned long phys_addr);
+
+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);
+void *kvm_create_kernel_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len, int log, int writable);
+
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem);
+int kvm_arch_create_default_phys_mem(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, int vcpu);
+void post_kvm_run(kvm_context_t kvm, int vcpu);
+int pre_kvm_run(kvm_context_t kvm, int vcpu);
+int handle_io_window(kvm_context_t kvm);
+int handle_debug(kvm_context_t kvm, int vcpu);
+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..304496d5b
--- /dev/null
+++ b/kvm/libkvm/kvm-ia64.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_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))
+
+#endif
diff --git a/kvm/libkvm/kvm-x86.h b/kvm/libkvm/kvm-x86.h
new file mode 100644
index 000000000..0769bb5b2
--- /dev/null
+++ b/kvm/libkvm/kvm-x86.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
+
+#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);
+
+#endif
diff --git a/kvm/libkvm/libkvm-ia64.c b/kvm/libkvm/libkvm-ia64.c
new file mode 100644
index 000000000..d7477d0e7
--- /dev/null
+++ b/kvm/libkvm/libkvm-ia64.c
@@ -0,0 +1,94 @@
+/*
+ * 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_default_phys_mem(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int zfd;
+
+ zfd = open("/dev/zero", O_RDONLY);
+ if (zfd == -1) {
+ perror("open /dev/zero");
+ return -1;
+ }
+ mmap(*vm_mem + 0xa8000, 0x8000, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_FIXED, zfd, 0);
+ close(zfd);
+
+ return 0;
+}
+
+
+int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
+ void **vm_mem)
+{
+ return 0;
+}
+
+void *kvm_create_kernel_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len, int log, int writable)
+{
+ return NULL;
+}
+
+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 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");
+}
diff --git a/kvm/libkvm/libkvm-x86.c b/kvm/libkvm/libkvm-x86.c
new file mode 100644
index 000000000..ca56adb9c
--- /dev/null
+++ b/kvm/libkvm/libkvm-x86.c
@@ -0,0 +1,504 @@
+#include "libkvm.h"
+#include "kvm-x86.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_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
+ void **vm_mem)
+{
+ unsigned long dosmem = 0xa0000;
+ unsigned long exmem = 0xc0000;
+ unsigned long pcimem = 0xe0000000;
+ int r;
+ int tss_ext;
+ struct kvm_memory_region low_memory = {
+ .memory_size = memory < dosmem ? memory : dosmem,
+ .guest_phys_addr = 0,
+ };
+ struct kvm_memory_region extended_memory = {
+ .memory_size = memory < exmem ? 0 : memory - exmem,
+ .guest_phys_addr = exmem,
+ };
+ struct kvm_memory_region above_4g_memory = {
+ .memory_size = memory < pcimem ? 0 : memory - pcimem,
+ .guest_phys_addr = 0x100000000ULL,
+ };
+
+#ifdef KVM_CAP_SET_TSS_ADDR
+ tss_ext = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+#else
+ tss_ext = 0;
+#endif
+
+ if (memory >= pcimem)
+ extended_memory.memory_size = pcimem - exmem;
+
+ /* 640K should be enough. */
+ low_memory.slot = get_free_slot(kvm);
+ r = ioctl(kvm->vm_fd, KVM_SET_MEMORY_REGION, &low_memory);
+ if (r == -1) {
+ fprintf(stderr, "kvm_create_memory_region: %m\n");
+ return -1;
+ }
+ register_slot(low_memory.slot, low_memory.guest_phys_addr,
+ low_memory.memory_size, 0, 0, 0);
+
+
+ if (extended_memory.memory_size) {
+ if (tss_ext > 0)
+ extended_memory.slot = get_free_slot(kvm);
+ else
+ extended_memory.slot = 0;
+ r = ioctl(kvm->vm_fd, KVM_SET_MEMORY_REGION, &extended_memory);
+ if (r == -1) {
+ fprintf(stderr, "kvm_create_memory_region: %m\n");
+ return -1;
+ }
+ register_slot(extended_memory.slot,
+ extended_memory.guest_phys_addr,
+ extended_memory.memory_size, 0, 0, 0);
+ }
+
+ if (above_4g_memory.memory_size) {
+ above_4g_memory.slot = get_free_slot(kvm);
+ r = ioctl(kvm->vm_fd, KVM_SET_MEMORY_REGION, &above_4g_memory);
+ if (r == -1) {
+ fprintf(stderr, "kvm_create_memory_region: %m\n");
+ return -1;
+ }
+ register_slot(above_4g_memory.slot,
+ above_4g_memory.guest_phys_addr,
+ above_4g_memory.memory_size, 0, 0, 0);
+ }
+
+ *vm_mem = mmap(NULL, memory, PROT_READ|PROT_WRITE, MAP_SHARED, kvm->vm_fd, 0);
+
+ return 0;
+}
+
+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) {
+ printf("kvm_init_tss: unable to set tss addr\n");
+ return r;
+ }
+
+ }
+#endif
+ return 0;
+}
+
+int kvm_arch_create_default_phys_mem(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int zfd;
+
+ zfd = open("/dev/zero", O_RDONLY);
+ if (zfd == -1) {
+ perror("open /dev/zero");
+ return -1;
+ }
+ mmap(*vm_mem + 0xa8000, 0x8000, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_FIXED, zfd, 0);
+ close(zfd);
+
+ 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;
+
+ return 0;
+}
+
+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
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+void *kvm_create_kernel_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_memory_region memory = {
+ .memory_size = len,
+ .guest_phys_addr = phys_start,
+ .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0,
+ };
+
+ memory.slot = get_free_slot(kvm);
+ r = ioctl(kvm->vm_fd, KVM_SET_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "create_kernel_phys_mem: %s", strerror(errno));
+ return 0;
+ }
+ register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size,
+ 0, 0, memory.flags);
+
+ if (writable)
+ prot |= PROT_WRITE;
+
+ ptr = mmap(NULL, len, prot, MAP_SHARED, kvm->vm_fd, phys_start);
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "create_kernel_phys_mem: %s", strerror(errno));
+ return 0;
+ }
+
+ return ptr;
+}
+
+int kvm_create_memory_alias(kvm_context_t kvm,
+ uint64_t phys_addr,
+ 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;
+
+ alias.slot = get_slot(phys_addr);
+
+ r = ioctl(fd, KVM_SET_MEMORY_ALIAS, &alias);
+ if (r == -1)
+ return -errno;
+
+ return 0;
+}
+
+int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_addr)
+{
+ return kvm_create_memory_alias(kvm, phys_addr, 0, 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
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+#define CR0_PE_MASK (1ULL<<0)
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r;
+ unsigned char code[50];
+ int back_offset;
+ char code_str[sizeof(code) * 3 + 1];
+ unsigned long rip;
+
+ r = ioctl(fd, KVM_GET_SREGS, &sregs);
+ if (r == -1) {
+ perror("KVM_GET_SREGS");
+ return;
+ }
+ if (sregs.cr0 & CR0_PE_MASK)
+ return;
+
+ r = ioctl(fd, KVM_GET_REGS, &regs);
+ 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;
+ memcpy(code, kvm->physical_memory + rip - back_offset, sizeof code);
+ *code_str = 0;
+ for (r = 0; r < sizeof code; ++r) {
+ if (r == back_offset)
+ strcat(code_str, " -->");
+ sprintf(code_str + strlen(code_str), " %02x", code[r]);
+ }
+ 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, &regs);
+ 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_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;
+}
diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
new file mode 100644
index 000000000..93d7b6bd4
--- /dev/null
+++ b/kvm/libkvm/libkvm.c
@@ -0,0 +1,1031 @@
+/*
+ * 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 "libkvm.h"
+
+#if defined(__x86_64__) || defined(__i386__)
+#include "kvm-x86.h"
+#endif
+
+#if defined(__ia64__)
+#include "kvm-ia64.h"
+#endif
+
+int kvm_abi = EXPECTED_KVM_API_VERSION;
+int kvm_page_size;
+
+struct slot_info {
+ unsigned long phys_addr;
+ unsigned long len;
+ int user_alloc;
+ unsigned long userspace_addr;
+ unsigned flags;
+};
+
+struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS];
+
+void init_slots(void)
+{
+ int i;
+
+ for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i)
+ slots[i].len = 0;
+}
+
+int get_free_slot(kvm_context_t kvm)
+{
+ int i;
+ int tss_ext;
+
+#ifdef KVM_CAP_SET_TSS_ADDR
+ 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;
+}
+
+void register_slot(int slot, unsigned long phys_addr, unsigned long len,
+ int user_alloc, unsigned long userspace_addr, unsigned flags)
+{
+ slots[slot].phys_addr = phys_addr;
+ slots[slot].len = len;
+ slots[slot].user_alloc = user_alloc;
+ slots[slot].userspace_addr = userspace_addr;
+ slots[slot].flags = flags;
+}
+
+void free_slot(int slot)
+{
+ slots[slot].len = 0;
+}
+
+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) >= phys_addr)
+ return i;
+ }
+ return -1;
+}
+
+int get_intersecting_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) > phys_addr)
+ return i;
+ return -1;
+}
+
+/*
+ * dirty pages logging control
+ */
+static int kvm_dirty_pages_log_change(kvm_context_t kvm, unsigned long phys_addr
+ , __u32 flag)
+{
+ int r;
+ int slot;
+
+ slot = get_slot(phys_addr);
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__);
+ return 1;
+ }
+ flag |= slots[slot].flags;
+#ifdef KVM_CAP_USER_MEMORY
+ if (slots[slot].user_alloc) {
+ 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 = flag,
+ };
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
+ }
+#endif
+ if (!slots[slot].user_alloc) {
+ struct kvm_memory_region mem = {
+ .slot = slot,
+ .memory_size = slots[slot].len,
+ .guest_phys_addr = slots[slot].phys_addr,
+ .flags = flag,
+ };
+ r = ioctl(kvm->vm_fd, KVM_SET_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, __u32 flag)
+{
+ int i, r;
+
+ for (i=r=0; i<KVM_MAX_NUM_MEM_REGIONS && r==0; i++) {
+ if (slots[i].len)
+ r = kvm_dirty_pages_log_change(kvm, slots[i].phys_addr,
+ flag);
+ }
+ return r;
+}
+
+/**
+ * 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_MEM_LOG_DIRTY_PAGES);
+}
+
+/**
+ * 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, 0);
+}
+
+
+kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
+ void *opaque)
+{
+ int fd;
+ kvm_context_t kvm;
+ int r;
+
+ 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));
+ kvm->fd = fd;
+ kvm->vm_fd = -1;
+ kvm->callbacks = callbacks;
+ kvm->opaque = opaque;
+ kvm->dirty_pages_log_all = 0;
+ kvm->no_irqchip_creation = 0;
+
+ 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;
+}
+
+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;
+
+ 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)
+{
+ unsigned long memory = (phys_mem_bytes + PAGE_SIZE - 1) & PAGE_MASK;
+ int r;
+
+#ifdef KVM_CAP_USER_MEMORY
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
+ if (r > 0)
+ return 0;
+ else
+#endif
+ r = kvm_alloc_kernel_memory(kvm, memory, vm_mem);
+ if (r < 0)
+ return r;
+
+ r = kvm_arch_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0)
+ return r;
+
+ kvm->physical_memory = *vm_mem;
+ return 0;
+}
+
+int kvm_check_extension(kvm_context_t kvm, int ext)
+{
+ int ret;
+
+ ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, ext);
+ if (ret > 0)
+ return 1;
+ 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_in_kernel = 1;
+ else
+ printf("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);
+ r = kvm_create_vcpu(kvm, 0);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+
+#ifdef KVM_CAP_USER_MEMORY
+
+void *kvm_create_userspace_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;
+
+ ptr = mmap(NULL, len, prot, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "create_userspace_phys_mem: %s", strerror(errno));
+ return 0;
+ }
+
+ memset(ptr, 0, len);
+
+ memory.userspace_addr = (unsigned long)ptr;
+ memory.slot = get_free_slot(kvm);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "create_userspace_phys_mem: %s", strerror(errno));
+ return 0;
+ }
+ register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size,
+ 1, memory.userspace_addr, memory.flags);
+
+ return ptr;
+}
+
+#endif
+
+void *kvm_create_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len, int log, int writable)
+{
+#ifdef KVM_CAP_USER_MEMORY
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
+ if (r > 0)
+ return kvm_create_userspace_phys_mem(kvm, phys_start, len,
+ log, writable);
+ else
+#endif
+ return kvm_create_kernel_phys_mem(kvm, phys_start, len,
+ log, writable);
+}
+
+int kvm_is_intersecting_mem(kvm_context_t kvm, unsigned long phys_start)
+{
+ return get_intersecting_slot(phys_start) != -1;
+}
+
+int kvm_is_allocated_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len)
+{
+ int slot;
+
+ slot = get_slot(phys_start);
+ if (slot == -1)
+ return 0;
+ if (slots[slot].len == len)
+ return 1;
+ return 0;
+}
+
+int kvm_create_mem_hole(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len)
+{
+#ifdef KVM_CAP_USER_MEMORY
+ int slot;
+ int r;
+ struct kvm_userspace_memory_region rmslot;
+ struct kvm_userspace_memory_region newslot1;
+ struct kvm_userspace_memory_region newslot2;
+
+ len = (len + PAGE_SIZE - 1) & PAGE_MASK;
+
+ slot = get_intersecting_slot(phys_start);
+ /* no need to create hole, as there is already hole */
+ if (slot == -1)
+ return 0;
+
+ memset(&rmslot, 0, sizeof(struct kvm_userspace_memory_region));
+ memset(&newslot1, 0, sizeof(struct kvm_userspace_memory_region));
+ memset(&newslot2, 0, sizeof(struct kvm_userspace_memory_region));
+
+ rmslot.guest_phys_addr = slots[slot].phys_addr;
+ rmslot.slot = slot;
+
+ newslot1.guest_phys_addr = slots[slot].phys_addr;
+ newslot1.memory_size = phys_start - slots[slot].phys_addr;
+ newslot1.slot = slot;
+ newslot1.userspace_addr = slots[slot].userspace_addr;
+ newslot1.flags = slots[slot].flags;
+
+ newslot2.guest_phys_addr = newslot1.guest_phys_addr +
+ newslot1.memory_size + len;
+ newslot2.memory_size = slots[slot].phys_addr +
+ slots[slot].len - newslot2.guest_phys_addr;
+ newslot2.userspace_addr = newslot1.userspace_addr +
+ newslot1.memory_size;
+ newslot2.slot = get_free_slot(kvm);
+ newslot2.flags = newslot1.flags;
+
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &rmslot);
+ if (r == -1) {
+ fprintf(stderr, "kvm_create_mem_hole: %s\n", strerror(errno));
+ return -1;
+ }
+ free_slot(slot);
+
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &newslot1);
+ if (r == -1) {
+ fprintf(stderr, "kvm_create_mem_hole: %s\n", strerror(errno));
+ return -1;
+ }
+ register_slot(newslot1.slot, newslot1.guest_phys_addr,
+ newslot1.memory_size, 1, newslot1.userspace_addr,
+ newslot1.flags);
+
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &newslot2);
+ if (r == -1) {
+ fprintf(stderr, "kvm_create_mem_hole: %s\n", strerror(errno));
+ return -1;
+ }
+ register_slot(newslot2.slot, newslot2.guest_phys_addr,
+ newslot2.memory_size, 1, newslot2.userspace_addr,
+ newslot2.flags);
+#endif
+ return 0;
+}
+
+int kvm_register_userspace_phys_mem(kvm_context_t kvm,
+ unsigned long phys_start, void *userspace_addr,
+ unsigned long len, int log)
+{
+
+#ifdef KVM_CAP_USER_MEMORY
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = len,
+ .guest_phys_addr = phys_start,
+ .userspace_addr = (intptr_t)userspace_addr,
+ .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0,
+ };
+ int r;
+
+ if (!kvm->physical_memory)
+ kvm->physical_memory = userspace_addr - phys_start;
+
+ memory.slot = get_free_slot(kvm);
+ 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,
+ 1, memory.userspace_addr, memory.flags);
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+
+/* 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;
+ struct kvm_memory_region *mem;
+
+ slot = get_slot(phys_start);
+
+ if (slot >= KVM_MAX_NUM_MEM_REGIONS) {
+ 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%llx\n",
+ __FUNCTION__, phys_start, mem->guest_phys_addr);
+ phys_start = mem->guest_phys_addr;
+ }
+ kvm_create_phys_mem(kvm, phys_start, 0, 0, 0);
+}
+
+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);
+}
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+#define BITMAP_SIZE(m) (ALIGN(((m)/PAGE_SIZE), sizeof(long) * 8) / 8)
+
+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 && slots[i].phys_addr >= phys_addr) &&
+ (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;
+}
+
+int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *buf)
+{
+ int slot;
+
+ slot = get_slot(phys_addr);
+#ifdef KVM_GET_MEM_MAP
+ return kvm_get_map(kvm, KVM_GET_MEM_MAP, slot, buf);
+#else /* not KVM_GET_MEM_MAP ==> fake it: all pages exist */
+ unsigned long i, n, m, npages;
+ unsigned char v;
+
+ if (slot >= KVM_MAX_NUM_MEM_REGIONS) {
+ errno = -EINVAL;
+ return -1;
+ }
+ npages = slots[slot].len / PAGE_SIZE;
+ n = npages / 8;
+ m = npages % 8;
+ memset(buf, 0xff, n); /* all pages exist */
+ v = 0;
+ for (i=0; i<=m; i++) /* last byte may not be "aligned" */
+ v |= 1<<(7-i);
+ if (v)
+ *(unsigned char*)(buf+n) = v;
+ return 0;
+#endif /* KVM_GET_MEM_MAP */
+}
+
+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 addr,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 && slots[i].phys_addr >= phys_addr &&
+ (slots[i].phys_addr + slots[i].len) <= end_addr) {
+ r = kvm_get_mem_map(kvm, slots[i].phys_addr, 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)
+{
+ 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_IRQ_LINE, &event);
+ if (r == -1)
+ perror("kvm_set_irq_level");
+ 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)
+{
+ return kvm->callbacks->debug(kvm->opaque, vcpu);
+}
+
+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);
+}
+
+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;
+ int r = -1;
+
+ /* hack: Red Hat 7.1 generates these wierd accesses. */
+ if (addr == 0xa0000 && kvm_run->mmio.len == 3)
+ return 0;
+
+ if (kvm_run->mmio.is_write) {
+ switch (kvm_run->mmio.len) {
+ case 1:
+ r = kvm->callbacks->writeb(kvm->opaque, addr, *(uint8_t *)data);
+ break;
+ case 2:
+ r = kvm->callbacks->writew(kvm->opaque, addr, *(uint16_t *)data);
+ break;
+ case 4:
+ r = kvm->callbacks->writel(kvm->opaque, addr, *(uint32_t *)data);
+ break;
+ case 8:
+ r = kvm->callbacks->writeq(kvm->opaque, addr, *(uint64_t *)data);
+ break;
+ }
+ } else {
+ switch (kvm_run->mmio.len) {
+ case 1:
+ r = kvm->callbacks->readb(kvm->opaque, addr, (uint8_t *)data);
+ break;
+ case 2:
+ r = kvm->callbacks->readw(kvm->opaque, addr, (uint16_t *)data);
+ break;
+ case 4:
+ r = kvm->callbacks->readl(kvm->opaque, addr, (uint32_t *)data);
+ break;
+ case 8:
+ r = kvm->callbacks->readq(kvm->opaque, addr, (uint64_t *)data);
+ break;
+ }
+ }
+ return r;
+}
+
+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, int vcpu)
+{
+ return kvm->callbacks->shutdown(kvm->opaque, vcpu);
+}
+
+int try_push_interrupts(kvm_context_t kvm)
+{
+ return kvm->callbacks->try_push_interrupts(kvm->opaque);
+}
+
+void post_kvm_run(kvm_context_t kvm, int vcpu)
+{
+ kvm->callbacks->post_kvm_run(kvm->opaque, vcpu);
+}
+
+int pre_kvm_run(kvm_context_t kvm, int vcpu)
+{
+ return kvm->callbacks->pre_kvm_run(kvm->opaque, vcpu);
+}
+
+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)
+{
+ int r;
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_run *run = kvm->run[vcpu];
+
+again:
+ if (!kvm->irqchip_in_kernel)
+ run->request_interrupt_window = try_push_interrupts(kvm);
+ r = pre_kvm_run(kvm, vcpu);
+ if (r)
+ return r;
+ r = ioctl(fd, KVM_RUN, 0);
+ post_kvm_run(kvm, vcpu);
+
+ if (r == -1 && errno != EINTR && errno != EAGAIN) {
+ r = -errno;
+ printf("kvm_run: %m\n");
+ return r;
+ }
+ if (r == -1) {
+ r = handle_io_window(kvm);
+ goto more;
+ }
+ 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);
+ 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);
+ 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, vcpu);
+ break;
+ 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);
+}
+
+int kvm_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_debug_guest *dbg)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_DEBUG_GUEST, dbg);
+}
+
+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;
+}
diff --git a/kvm/libkvm/libkvm.h b/kvm/libkvm/libkvm.h
new file mode 100644
index 000000000..110912a8d
--- /dev/null
+++ b/kvm/libkvm/libkvm.h
@@ -0,0 +1,537 @@
+/** \file libkvm.h
+ * libkvm API
+ */
+
+#ifndef LIBKVM_H
+#define LIBKVM_H
+
+#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);
+ /// For 8bit memory reads from unmapped memory (For MMIO devices)
+ int (*readb)(void *opaque, uint64_t addr, uint8_t *data);
+ /// For 16bit memory reads from unmapped memory (For MMIO devices)
+ int (*readw)(void *opaque, uint64_t addr, uint16_t *data);
+ /// For 32bit memory reads from unmapped memory (For MMIO devices)
+ int (*readl)(void *opaque, uint64_t addr, uint32_t *data);
+ /// For 64bit memory reads from unmapped memory (For MMIO devices)
+ int (*readq)(void *opaque, uint64_t addr, uint64_t *data);
+ /// For 8bit memory writes to unmapped memory (For MMIO devices)
+ int (*writeb)(void *opaque, uint64_t addr, uint8_t data);
+ /// For 16bit memory writes to unmapped memory (For MMIO devices)
+ int (*writew)(void *opaque, uint64_t addr, uint16_t data);
+ /// For 32bit memory writes to unmapped memory (For MMIO devices)
+ int (*writel)(void *opaque, uint64_t addr, uint32_t data);
+ /// For 64bit memory writes to unmapped memory (For MMIO devices)
+ int (*writeq)(void *opaque, uint64_t addr, uint64_t data);
+ int (*debug)(void *opaque, int vcpu);
+ /*!
+ * \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, int vcpu);
+ int (*io_window)(void *opaque);
+ int (*try_push_interrupts)(void *opaque);
+ void (*post_kvm_run)(void *opaque, int vcpu);
+ int (*pre_kvm_run)(void *opaque, int vcpu);
+};
+
+/*!
+ * \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 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);
+
+/*!
+ * \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);
+
+/*!
+ * \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);
+
+int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
+
+#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 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);
+
+/*!
+ * \breif 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);
+int kvm_is_intersecting_mem(kvm_context_t kvm, unsigned long phys_start);
+int kvm_is_allocated_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len);
+int kvm_create_mem_hole(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len);
+int kvm_register_userspace_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));
+
+/*!
+ * \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_addr,
+ 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_addr);
+
+/*!
+ * \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);
+
+/*!
+ * \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_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
+
+#endif
+
+#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/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..e6ba27da0
--- /dev/null
+++ b/kvm/user/Makefile
@@ -0,0 +1,48 @@
+
+include config.mak
+
+DESTDIR :=
+
+.PHONY: arch_clean clean
+
+#make sure env CFLAGS variable is not used
+CFLAGS =
+
+#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 ../libkvm
+
+LDFLAGS += $(CFLAGS) -L ../libkvm
+
+CXXFLAGS = $(autodepend-flags)
+
+autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
+
+FLATLIBS = $(TEST_DIR)/libcflat.a
+
+kvmctl: LDFLAGS += -pthread -lrt
+
+kvmctl: $(kvmctl_objs)
+ $(CC) $(LDFLAGS) $^ -o $@
+
+%.flat: %.o $(FLATLIBS)
+ $(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,flat.lds $^ $(FLATLIBS)
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c -nostdlib -o $@ $^
+
+-include .*.d
+
+clean: arch_clean
+ $(RM) kvmctl *.o *.a .*.d
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..090851776
--- /dev/null
+++ b/kvm/user/config-i386.mak
@@ -0,0 +1,11 @@
+TEST_DIR=test/x86
+cstart.o = $(TEST_DIR)/cstart.o
+bits = 32
+ldarch = elf32-i386
+CFLAGS += -m32
+CFLAGS += -D__i386__
+CFLAGS += -I $(KERNELDIR)/include
+
+tests=
+
+include config-x86-common.mak
diff --git a/kvm/user/config-x86-common.mak b/kvm/user/config-x86-common.mak
new file mode 100644
index 000000000..ec3f1acbe
--- /dev/null
+++ b/kvm/user/config-x86-common.mak
@@ -0,0 +1,45 @@
+#This is a make file with common rules for both x86 & x86-64
+
+all: kvmctl test_cases
+
+kvmctl_objs= main.o ../libkvm/libkvm.a
+
+balloon_ctl: balloon_ctl.o
+
+tests-common = $(TEST_DIR)/bootstrap \
+ $(TEST_DIR)/vmexit.flat \
+ $(TEST_DIR)/smp.flat $(TEST_DIR)/port80.flat
+
+test_cases: $(tests-common) $(tests)
+
+$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I$(TEST_DIR)/lib
+
+$(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)/smp.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)/libcflat.a: $(TEST_DIR)/lib/exit.o $(TEST_DIR)/lib/printf.o \
+ $(TEST_DIR)/lib/smp.o $(TEST_DIR)/lib/string.o
+ ar rcs $@ $^
+
+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..9f37e6f41
--- /dev/null
+++ b/kvm/user/config-x86_64.mak
@@ -0,0 +1,14 @@
+TEST_DIR=test/x86
+cstart.o = $(TEST_DIR)/cstart64.o
+bits = 64
+ldarch = elf64-x86-64
+CFLAGS += -m64
+CFLAGS += -D__x86_64__
+CFLAGS += -I $(KERNELDIR)/include
+
+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
+
+include config-x86-common.mak
diff --git a/kvm/user/configure b/kvm/user/configure
new file mode 100755
index 000000000..ff102683a
--- /dev/null
+++ b/kvm/user/configure
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+prefix=/usr/local
+kerneldir=/lib/modules/$(uname -r)/build
+cc=gcc
+ld=ld
+arch=`uname -m | sed -e s/i.86/i386/`
+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"
+ ;;
+ --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
+CC=$cross_prefix$cc
+LD=$cross_prefix$ld
+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/main.c b/kvm/user/main.c
new file mode 100644
index 000000000..213b019f5
--- /dev/null
+++ b/kvm/user/main.c
@@ -0,0 +1,666 @@
+/*
+ * 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/x86/lib/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>
+
+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)
+
+#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];
+};
+
+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 {
+ pid_t tid;
+ sem_t sipi_sem;
+};
+
+struct vcpu_info *vcpus;
+
+static uint32_t apic_sipi_addr;
+
+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;
+}
+
+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);
+}
+
+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;
+}
+
+static int test_debug(void *opaque, int vcpu)
+{
+ printf("test_debug\n");
+ return 0;
+}
+
+static int test_halt(void *opaque, int vcpu)
+{
+ int n;
+
+ sigwait(&ipi_sigmask, &n);
+ kvm_inject_irq(kvm, vcpu, apic_ipi_vector);
+ 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, int vcpu)
+{
+}
+
+static int test_pre_kvm_run(void *opaque, int vcpu)
+{
+ return 0;
+}
+
+static int test_mem_read(uint64_t addr, void *data, unsigned 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(uint64_t addr, void *data, unsigned 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_readb(void *opaque, uint64_t addr, uint8_t *data)
+{
+ return test_mem_read(addr, data, 1);
+}
+
+static int test_readw(void *opaque, uint64_t addr, uint16_t *data)
+{
+ return test_mem_read(addr, data, 2);
+}
+
+static int test_readl(void *opaque, uint64_t addr, uint32_t *data)
+{
+ return test_mem_read(addr, data, 4);
+
+}
+static int test_readq(void *opaque, uint64_t addr, uint64_t *data)
+{
+ return test_mem_read(addr, data, 8);
+}
+
+static int test_writeb(void *opaque, uint64_t addr, uint8_t data)
+{
+ return test_mem_write(addr, &data, 1);
+}
+
+static int test_writew(void *opaque, uint64_t addr, uint16_t data)
+{
+ return test_mem_write(addr, &data, 2);
+}
+
+static int test_writel(void *opaque, uint64_t addr, uint32_t data)
+{
+ return test_mem_write(addr, &data, 4);
+}
+
+static int test_writeq(void *opaque, uint64_t addr, uint64_t data)
+{
+ return test_mem_write(addr, &data, 8);
+}
+
+static struct kvm_callbacks test_callbacks = {
+ .inb = test_inb,
+ .inw = test_inw,
+ .inl = test_inl,
+ .outb = test_outb,
+ .outw = test_outw,
+ .outl = test_outl,
+ .readb = test_readb,
+ .readw = test_readw,
+ .readl = test_readl,
+ .readq = test_readq,
+ .writeb = test_writeb,
+ .writew = test_writew,
+ .writel = test_writel,
+ .writeq = test_writeq,
+ .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,
+};
+
+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, &regs);
+ 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].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, &regs);
+ regs.rip = apic_sipi_addr;
+ kvm_set_regs(kvm, n, &regs);
+ kvm_run(kvm, 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-devel@lists.sourceforge.net>.\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();
+
+ sem_init(&init_sem, 0, 0);
+ init_vcpu(0);
+ for (i = 1; i < ncpus; ++i)
+ start_vcpu(i);
+ for (i = 0; i < ncpus; ++i)
+ sem_wait(&init_sem);
+
+ kvm_run(kvm, 0);
+
+ return 0;
+}
diff --git a/kvm/user/test/x86/access.c b/kvm/user/test/x86/access.c
new file mode 100644
index 000000000..adfb944ab
--- /dev/null
+++ b/kvm/user/test/x86/access.c
@@ -0,0 +1,580 @@
+
+#include "smp.h"
+#include "printf.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_PDE_PRESENT,
+ AC_PDE_WRITABLE,
+ AC_PDE_USER,
+ AC_PDE_ACCESSED,
+ AC_PDE_DIRTY,
+ AC_PDE_PSE,
+ AC_PDE_NX,
+
+ 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_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_ACCESS_WRITE] = "write",
+ [AC_ACCESS_USER] = "user",
+ [AC_ACCESS_FETCH] = "fetch",
+ [AC_ACCESS_TWICE] = "twice",
+ [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();
+
+ 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;
+ 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;
+ 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;
+
+ if (at->flags[AC_ACCESS_TWICE]) {
+ if (at->flags[AC_PDE_PRESENT]) {
+ at->expected_pde |= PT_ACCESSED_MASK;
+ if (at->flags[AC_PTE_PRESENT])
+ 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;
+ }
+
+ 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;
+ }
+
+ 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]);
+
+ 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()
+{
+ 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(ac_test_run);
+ r = ac_test_run();
+ return r ? 0 : 1;
+}
diff --git a/kvm/user/test/x86/bootstrap.S b/kvm/user/test/x86/bootstrap.S
new file mode 100644
index 000000000..7f09fb9cb
--- /dev/null
+++ b/kvm/user/test/x86/bootstrap.S
@@ -0,0 +1,137 @@
+/*
+ * minimal bootstrap to set up flat 32-bit protected mode
+ */
+
+#include "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..16641c248
--- /dev/null
+++ b/kvm/user/test/x86/cstart64.S
@@ -0,0 +1,168 @@
+
+#include "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..d3fb9993d
--- /dev/null
+++ b/kvm/user/test/x86/emulator.c
@@ -0,0 +1,157 @@
+#include "ioram.h"
+#include "vm.h"
+#include "printf.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;
+ int i;
+
+ 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"
+ "mov %%rsp, %[new_stack_top] \n\t"
+ "mov %[tmp], %%rsp"
+ : [new_stack_top]"=r"(new_stack_top)
+ : [tmp]"r"(tmp), [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);
+}
+
+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_cr8();
+
+ 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..1a51b6daa
--- /dev/null
+++ b/kvm/user/test/x86/hypercall.c
@@ -0,0 +1,19 @@
+#include "printf.h"
+
+#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
+
+static inline long kvm_hypercall0(unsigned int nr)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr));
+ return ret;
+}
+
+int main(int ac, char **av)
+{
+ kvm_hypercall0(-1u);
+ printf("Hypercall: 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/lib/apic.h b/kvm/user/test/x86/lib/apic.h
new file mode 100644
index 000000000..eed63baef
--- /dev/null
+++ b/kvm/user/test/x86/lib/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/x86/lib/exit.c b/kvm/user/test/x86/lib/exit.c
new file mode 100644
index 000000000..2ae43ea70
--- /dev/null
+++ b/kvm/user/test/x86/lib/exit.c
@@ -0,0 +1,5 @@
+
+void exit(int code)
+{
+ asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4));
+}
diff --git a/kvm/user/test/x86/lib/printf.c b/kvm/user/test/x86/lib/printf.c
new file mode 100644
index 000000000..5caec246a
--- /dev/null
+++ b/kvm/user/test/x86/lib/printf.c
@@ -0,0 +1,171 @@
+#include "printf.h"
+#include "smp.h"
+#include <stdarg.h>
+#include "string.h"
+
+static struct spinlock lock;
+
+void print(const char *s);
+
+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 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 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)
+{
+ int n;
+ pstream_t s;
+
+ s.buffer = buf;
+ s.remain = size - 1;
+ s.added = 0;
+ while (*fmt) {
+ char f = *fmt++;
+
+ if (f != '%') {
+ addchar(&s, f);
+ continue;
+ }
+ f = *fmt++;
+ switch (f) {
+ case '%':
+ addchar(&s, '%');
+ break;
+ case '\0':
+ --fmt;
+ break;
+ case 'd':
+ print_int(&s, va_arg(va, int), 10);
+ break;
+ case 'x':
+ print_unsigned(&s, va_arg(va, int), 16);
+ 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;
+}
+
+void print_serial(const char *buf)
+{
+ unsigned long len = strlen(buf);
+
+ asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+}
+
+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);
+ spin_lock(&lock);
+ print_serial(buf);
+ spin_unlock(&lock);
+ return r;
+}
diff --git a/kvm/user/test/x86/lib/printf.h b/kvm/user/test/x86/lib/printf.h
new file mode 100644
index 000000000..7d4b779ca
--- /dev/null
+++ b/kvm/user/test/x86/lib/printf.h
@@ -0,0 +1,2 @@
+
+int printf(const char *fmt, ...);
diff --git a/kvm/user/test/x86/lib/smp.c b/kvm/user/test/x86/lib/smp.c
new file mode 100644
index 000000000..a4cbe6147
--- /dev/null
+++ b/kvm/user/test/x86/lib/smp.c
@@ -0,0 +1,151 @@
+
+
+#include "smp.h"
+#include "apic.h"
+#include "printf.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/x86/lib/smp.h b/kvm/user/test/x86/lib/smp.h
new file mode 100644
index 000000000..bcf76a357
--- /dev/null
+++ b/kvm/user/test/x86/lib/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/x86/lib/string.c b/kvm/user/test/x86/lib/string.c
new file mode 100644
index 000000000..83e99fefa
--- /dev/null
+++ b/kvm/user/test/x86/lib/string.c
@@ -0,0 +1,21 @@
+#include "string.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/x86/lib/string.h b/kvm/user/test/x86/lib/string.h
new file mode 100644
index 000000000..338d40ff3
--- /dev/null
+++ b/kvm/user/test/x86/lib/string.h
@@ -0,0 +1,7 @@
+#ifndef FLAT_LIB_STRING_H
+#define FLAT_LIB_STRING_H
+
+unsigned long strlen(const char *buf);
+char *strcat(char *dest, const char *src);
+
+#endif
diff --git a/kvm/user/test/x86/memtest1.S b/kvm/user/test/x86/memtest1.S
new file mode 100644
index 000000000..3821e867c
--- /dev/null
+++ b/kvm/user/test/x86/memtest1.S
@@ -0,0 +1,44 @@
+.text
+
+start:
+ mov $0x1000,%r8
+ mov $0x0a,%ecx
+
+init_page:
+ dec %ecx
+ jne no_io
+ mov $0x0,%al
+ out %al,$0x80
+ mov $0x0a,%ecx
+
+no_io:
+ mov %r8,(%r8)
+ add $0x1000,%r8
+ cmp $0x8000000,%r8
+ jne init_page
+ mov $0x1000,%r8
+ mov $0x0a,%ecx
+
+test_loop:
+ dec %ecx
+ jne no_io2
+ mov $0x0,%al
+ out %al,$0x80
+ mov $0x0a,%ecx
+
+no_io2:
+ mov (%r8),%r9
+ cmp %r8,%r9
+ jne err
+ add $0x1000,%r8
+ cmp $0x8000000,%r8
+ jne test_loop
+ mov $0x1000,%r8
+ jmp test_loop
+
+err:
+ mov $0xffffffffffffffff,%r12
+ mov $0xffffffffffffffff,%r13
+ mov $0x0,%al
+ out %al,$0x80
+ jmp err
diff --git a/kvm/user/test/x86/port80.c b/kvm/user/test/x86/port80.c
new file mode 100644
index 000000000..a1c4099ca
--- /dev/null
+++ b/kvm/user/test/x86/port80.c
@@ -0,0 +1,13 @@
+
+#include "printf.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/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..f6ef3e97d
--- /dev/null
+++ b/kvm/user/test/x86/smptest.c
@@ -0,0 +1,32 @@
+
+#include "smp.h"
+#include "printf.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/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..f6341cc4c
--- /dev/null
+++ b/kvm/user/test/x86/vmexit.c
@@ -0,0 +1,32 @@
+
+#include "printf.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)
+
+int main()
+{
+ int i;
+ unsigned long long t1, t2;
+
+ t1 = rdtsc();
+ for (i = 0; i < N; ++i)
+ asm volatile ("cpuid" : : : "eax", "ebx", "ecx", "edx");
+ t2 = rdtsc();
+ printf("vmexit latency: %d\n", (int)((t2 - t1) / N));
+ return 0;
+}
diff --git a/migration.c b/migration.c
new file mode 100644
index 000000000..17ad77ced
--- /dev/null
+++ b/migration.c
@@ -0,0 +1,1144 @@
+/*
+ * QEMU migration support
+ *
+ * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * 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/hw.h"
+#include "sysemu.h"
+#include "block.h"
+#include "console.h"
+#include "qemu-timer.h"
+#include "migration.h"
+#include "qemu_socket.h"
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+#endif
+
+#include <sys/wait.h>
+
+#define MIN_FINALIZE_SIZE (200 << 10)
+#define MAX_ITERATIONS 30
+#define MAX_RAPID_WRITES 5
+
+typedef struct MigrationState
+{
+ int fd;
+ int throttle_count;
+ int bps;
+ int updated_pages;
+ int last_updated_pages;
+ int iteration;
+ int n_buffer; /* number of bytes in @buffer already sent */
+ int l_buffer; /* number of bytes in @buffer to send */
+ int throttled;
+ int *has_error;
+ char buffer[TARGET_PAGE_SIZE + 4 + 4];
+ target_ulong addr;
+ QEMUTimer *timer;
+ void *opaque;
+ int detach;
+ int (*release)(void *opaque);
+ int rapid_writes;
+} MigrationState;
+
+static uint32_t max_throttle = (32 << 20);
+static MigrationState *current_migration;
+static int wait_for_message_timeout = 3000; /* 3 seconds */
+static int status; /* last migration status */
+
+enum { /* migration status values */
+ MIG_STAT_SUCCESS = 0,
+
+ /* src error codes */
+ MIG_STAT_INVALID_PARAMS = 1,
+ MIG_STAT_INVALID_ADDR = 2,
+ MIG_STAT_SOCKET_FAILED = 3,
+ MIG_STAT_SOCKOPT_FAILED = 4,
+ MIG_STAT_BIND_FAILED = 5,
+ MIG_STAT_LISTEN_FAILED = 6,
+ MIG_STAT_ACCEPT_FAILED = 7,
+ MIG_STAT_CONNECT_FAILED = 8,
+ MIG_STAT_WRITE_FAILED = 9,
+ MIG_STAT_READ_FAILED = 10,
+ MIG_STAT_CONNECTION_CLOSED = 11,
+ MIG_STAT_SELECT_FAILED = 12,
+ MIG_STAT_SELECT_TIMEOUT = 13,
+ MIG_STAT_SELECT_FD_NOT_SET = 14,
+
+ MIG_STAT_SAVEVM_FAILED = 15,
+ MIG_STAT_NO_MEM = 16,
+
+ MIG_STAT_MIGRATION_CANCEL = 20,
+
+ /* kvm error codes (on src) */
+ MIG_STAT_KVM_UPDATE_DIRTY_PAGES_LOG_FAILED = 101,
+ MIG_STAT_KVM_SET_DIRTY_TRACKING_FAILED = 102,
+ MIG_STAT_KVM_GET_PAGE_BITMAP = 103,
+
+ /* dst error codes */
+ MIG_STAT_DST_INVALID_PARAMS = 200 + MIG_STAT_INVALID_PARAMS,
+ MIG_STAT_DST_INVALID_ADDR = 200 + MIG_STAT_INVALID_ADDR,
+ MIG_STAT_DST_SOCKET_FAILED = 200 + MIG_STAT_SOCKET_FAILED,
+ MIG_STAT_DST_SOCKOPT_FAILED = 200 + MIG_STAT_SOCKOPT_FAILED,
+ MIG_STAT_DST_BIND_FAILED = 200 + MIG_STAT_BIND_FAILED,
+ MIG_STAT_DST_LISTEN_FAILED = 200 + MIG_STAT_LISTEN_FAILED,
+ MIG_STAT_DST_ACCEPT_FAILED = 200 + MIG_STAT_ACCEPT_FAILED,
+ MIG_STAT_DST_CONNECT_FAILED = 200 + MIG_STAT_CONNECT_FAILED,
+ MIG_STAT_DST_WRITE_FAILED = 200 + MIG_STAT_WRITE_FAILED,
+ MIG_STAT_DST_READ_FAILED = 200 + MIG_STAT_READ_FAILED,
+
+ MIG_STAT_DST_CONNECTION_CLOSED = 200 + MIG_STAT_CONNECTION_CLOSED,
+ MIG_STAT_DST_SELECT_FAILED = 200 + MIG_STAT_SELECT_FAILED,
+ MIG_STAT_DST_SELECT_TIMEOUT = 200 + MIG_STAT_SELECT_TIMEOUT,
+ MIG_STAT_DST_SELECT_FD_NOT_SET = 200 + MIG_STAT_SELECT_FD_NOT_SET,
+
+ MIG_STAT_DST_LOADVM_FAILED = 200 + MIG_STAT_SAVEVM_FAILED,
+ MIG_STAT_DST_NO_MEM = 200 + MIG_STAT_NO_MEM,
+
+ MIG_STAT_DST_GET_PAGE_FAILED = 230,
+ MIG_STAT_DST_GET_PAGE_UNKNOWN_TYPE = 231,
+ MIG_STAT_DST_MEM_SIZE_MISMATCH = 232,
+ MIG_STAT_DST_MEM_OUT_OF_RANGE = 233,
+};
+
+//#define MIGRATION_VERIFY
+#ifdef MIGRATION_VERIFY
+static int save_verify_memory(QEMUFile *f, void *opaque);
+static int load_verify_memory(QEMUFile *f, void *opaque, int version_id);
+#endif /* MIGRATION_VERIFY */
+
+/* QEMUFile migration implementation */
+
+static void migrate_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ MigrationState *s = opaque;
+ int offset = 0;
+
+ if (*s->has_error)
+ return;
+
+ while (offset < size) {
+ ssize_t len;
+
+ len = write(s->fd, buf + offset, size - offset);
+ if (len == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ term_printf("migration: write failed (%s)\n", strerror(errno));
+ *s->has_error = MIG_STAT_WRITE_FAILED;
+ break;
+ } else if (len == 0) {
+ term_printf("migration: other side closed connection\n");
+ *s->has_error = MIG_STAT_CONNECTION_CLOSED;
+ break;
+ }
+
+ offset += len;
+ }
+}
+
+static void migrate_close(void *opaque)
+{
+ MigrationState *s = opaque;
+
+ if (s->release)
+ s->release(s->opaque);
+
+ qemu_free(s);
+ current_migration = NULL;
+}
+
+/* Outgoing migration routines */
+static void migrate_finish(MigrationState *s)
+{
+ QEMUFile *f;
+ int ret = 0;
+ int *has_error = s->has_error;
+ int saved_vm_running = vm_running;
+ int detach = s->detach;
+
+ fcntl(s->fd, F_SETFL, 0);
+
+ if (! *has_error) {
+ vm_stop(0);
+ f = qemu_fopen(s, migrate_put_buffer, NULL, migrate_close);
+ do {
+ qemu_aio_flush();
+ } while (qemu_bh_poll());
+ bdrv_flush_all();
+#ifdef USE_KVM
+ if (kvm_allowed && !*s->has_error && kvm_update_dirty_pages_log())
+ *s->has_error = MIG_STAT_KVM_UPDATE_DIRTY_PAGES_LOG_FAILED;
+#endif
+ qemu_put_be32(f, 1);
+ ret = qemu_live_savevm_state(f);
+#ifdef MIGRATION_VERIFY
+ save_verify_memory(f, NULL);
+#endif /* MIGRATION_VERIFY */
+ qemu_fclose(f);
+ } else {
+ migrate_close(s);
+ }
+ status = *has_error;
+ if (ret && !status)
+ status = MIG_STAT_SAVEVM_FAILED;
+ if (status) {
+ term_printf("Migration failed! ret=%d error=%d\n", ret, *has_error);
+ if (saved_vm_running)
+ vm_start();
+ }
+ if (!detach)
+ monitor_resume();
+ qemu_free(has_error);
+ cpu_physical_memory_set_dirty_tracking(0);
+}
+
+static int migrate_write_buffer(MigrationState *s)
+{
+ if (*s->has_error)
+ return 0;
+
+ if (s->n_buffer < s->l_buffer) {
+ ssize_t len;
+ again:
+ len = write(s->fd, s->buffer + s->n_buffer, s->l_buffer - s->n_buffer);
+ if (len == -1) {
+ if (errno == EINTR)
+ goto again;
+ if (errno == EAGAIN)
+ return 1;
+ *s->has_error = MIG_STAT_WRITE_FAILED;
+ return 0;
+ }
+ if (len == 0) {
+ *s->has_error = MIG_STAT_CONNECTION_CLOSED;
+ return 0;
+ }
+
+ s->throttle_count += len;
+ s->n_buffer += len;
+ if (s->n_buffer < s->l_buffer)
+ goto again;
+ }
+
+ if (s->throttle_count > max_throttle) {
+ s->throttled = 1;
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int migrate_check_convergence(MigrationState *s)
+{
+ target_ulong addr;
+ int dirty_count = 0;
+
+ if ((s->iteration >= MAX_ITERATIONS) ||
+ (s->rapid_writes >= MAX_RAPID_WRITES) ) {
+ return 1;
+ }
+
+ for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (addr>=0xa0000) && (addr<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
+ if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG))
+ dirty_count++;
+ }
+
+ return ((dirty_count * TARGET_PAGE_SIZE) < MIN_FINALIZE_SIZE);
+}
+
+static int ram_page_is_homogeneous(uint32_t addr)
+{
+ int i, n;
+ uint32_t *p, v;
+
+ n = TARGET_PAGE_SIZE / sizeof(v);
+ p = (uint32 *)(phys_ram_base + addr);
+
+ v = p[0];
+ for (i=1; i<n; i++)
+ if (p[i] != v)
+ return 0;
+ return 1;
+}
+
+static void migrate_prepare_page(MigrationState *s)
+{
+ uint32_t value;
+ char type;
+ const char *buff;
+ int bufflen;
+
+ value = cpu_to_be32(s->addr);
+ memcpy(s->buffer, &value, 4);
+
+ if (ram_page_is_homogeneous(s->addr)) {
+ type = 1; /* keeping ram_get_page() values */
+ bufflen = 4;
+ }
+ else {
+ type = 0;
+ bufflen = TARGET_PAGE_SIZE;
+ }
+
+ buff = phys_ram_base + s->addr;
+ s->buffer[4] = type;
+ memcpy(s->buffer + 4 + 1, phys_ram_base + s->addr, bufflen);
+ s->n_buffer = 0;
+ s->l_buffer = 4 + 1 + bufflen;
+}
+
+static void migrate_write(void *opaque)
+{
+ MigrationState *s = opaque;
+
+ if (migrate_write_buffer(s))
+ return;
+
+#ifdef USE_KVM
+ if (kvm_allowed && !*s->has_error && kvm_update_dirty_pages_log())
+ *s->has_error = MIG_STAT_KVM_UPDATE_DIRTY_PAGES_LOG_FAILED;
+#endif
+
+ if (migrate_check_convergence(s) || *s->has_error) {
+ qemu_del_timer(s->timer);
+ qemu_free_timer(s->timer);
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ migrate_finish(s);
+ return;
+ }
+
+ while (s->addr < phys_ram_size) {
+#ifdef USE_KVM
+ if (kvm_allowed && (s->addr>=0xa0000) && (s->addr<0xc0000)) /* do not access video-addresses */
+ s->addr = 0xc0000;
+#endif
+
+ if (cpu_physical_memory_get_dirty(s->addr, MIGRATION_DIRTY_FLAG)) {
+ migrate_prepare_page(s);
+ cpu_physical_memory_reset_dirty(s->addr, s->addr + TARGET_PAGE_SIZE, MIGRATION_DIRTY_FLAG);
+
+ s->addr += TARGET_PAGE_SIZE;
+
+ s->updated_pages++;
+
+ if (migrate_write_buffer(s))
+ return;
+ } else
+ s->addr += TARGET_PAGE_SIZE;
+ }
+
+ if ((s->iteration) && (s->last_updated_pages <= s->updated_pages)) {
+ s->rapid_writes++; /* "dirt-speed" is faster than transfer speed */
+ if (max_throttle < (1 << 30))
+ max_throttle *= 2; /* try harder */
+ }
+ s->last_updated_pages = s->updated_pages;
+ s->updated_pages = 0;
+ s->addr = 0;
+ s->iteration++;
+}
+
+static void migrate_reset_throttle(void *opaque)
+{
+ MigrationState *s = opaque;
+
+ s->bps = s->throttle_count;
+
+ if (s->throttled) {
+ s->throttled = 0;
+ qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_write, s);
+ }
+ s->throttle_count = 0;
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
+}
+
+static int write_whole_buffer(int fd, const void *buff, size_t size)
+{
+ size_t offset = 0, len;
+
+ while (offset < size) {
+ len = write(fd, buff + offset, size - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+ if (len < 1)
+ return -EIO;
+
+ offset += len;
+ }
+ return !(offset == size); /* returns 0 on success */
+}
+
+static int bit_is_set(int bit, unsigned char *map)
+{
+ return map[bit/8] & (1 << (bit%8));
+}
+
+static int start_migration(MigrationState *s)
+{
+ uint32_t value = cpu_to_be32(phys_ram_size);
+ target_phys_addr_t addr;
+ int r;
+ unsigned char running = vm_running?2:1; /* 1 + vm_running */
+
+#ifdef USE_KVM
+ int n = 0;
+ unsigned char *phys_ram_page_exist_bitmap = NULL;
+ if (kvm_allowed) {
+ n = BITMAP_SIZE(phys_ram_size);
+ phys_ram_page_exist_bitmap = qemu_malloc(n);
+ if (!phys_ram_page_exist_bitmap) {
+ perror("failed to allocate page bitmap");
+ r = MIG_STAT_NO_MEM;
+ goto out;
+ }
+ if (kvm_get_phys_ram_page_bitmap(phys_ram_page_exist_bitmap)) {
+ r = MIG_STAT_KVM_GET_PAGE_BITMAP;
+ perror("kvm_get_mem_map failed");
+ goto out;
+ }
+ }
+#endif
+
+ r = MIG_STAT_WRITE_FAILED;
+ if (write_whole_buffer(s->fd, &running, sizeof(running)))
+ goto out;
+ if (write_whole_buffer(s->fd, &value, sizeof(value)))
+ goto out;
+
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ value = cpu_to_be32(n);
+ if (write_whole_buffer(s->fd, &value, sizeof(value)))
+ goto out;
+ if (write_whole_buffer(s->fd, phys_ram_page_exist_bitmap, n))
+ goto out;
+ }
+#endif
+ fcntl(s->fd, F_SETFL, O_NONBLOCK);
+
+ for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && !bit_is_set(addr>>TARGET_PAGE_BITS, phys_ram_page_exist_bitmap)) {
+ cpu_physical_memory_reset_dirty(addr,
+ addr + TARGET_PAGE_SIZE,
+ MIGRATION_DIRTY_FLAG);
+ continue;
+ }
+#endif
+ if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG))
+ cpu_physical_memory_set_dirty(addr);
+ }
+
+ if (cpu_physical_memory_set_dirty_tracking(1)) {
+ *s->has_error = MIG_STAT_KVM_SET_DIRTY_TRACKING_FAILED;
+ return -1;
+ }
+
+ s->addr = 0;
+ s->iteration = 0;
+ s->updated_pages = 0;
+ s->last_updated_pages = 0;
+ s->n_buffer = s->l_buffer = 0;
+ s->rapid_writes = 0;
+ s->timer = qemu_new_timer(rt_clock, migrate_reset_throttle, s);
+
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock));
+ qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_write, s);
+
+ out:
+#ifdef USE_KVM
+ if (phys_ram_page_exist_bitmap)
+ qemu_free(phys_ram_page_exist_bitmap);
+#endif
+ return r;
+}
+
+static MigrationState *migration_init_fd(int detach, int fd)
+{
+ MigrationState *s;
+
+ s = qemu_mallocz(sizeof(MigrationState));
+ if (s == NULL) {
+ term_printf("Allocation error\n");
+ return NULL;
+ }
+
+ s->fd = fd;
+ s->has_error = qemu_mallocz(sizeof(int));
+ if (s->has_error == NULL) {
+ term_printf("malloc failed (for has_error)\n");
+ return NULL;
+ }
+ s->detach = detach;
+
+ current_migration = s;
+
+ if (start_migration(s) == -1) {
+ term_printf("Could not start migration\n");
+ return NULL;
+ }
+
+ if (!detach)
+ monitor_suspend();
+
+ return s;
+}
+
+typedef struct MigrationCmdState
+{
+ int fd;
+ pid_t pid;
+} MigrationCmdState;
+
+static int cmd_release(void *opaque)
+{
+ MigrationCmdState *c = opaque;
+ int status, ret;
+
+ close(c->fd);
+
+again:
+ ret = waitpid(c->pid, &status, 0);
+ if (ret == -1 && errno == EINTR)
+ goto again;
+
+ if (ret == -1) {
+ term_printf("migration: waitpid failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ /* FIXME: check and uncomment
+ * if (WIFEXITED(status))
+ * status = WEXITSTATUS(status);
+ */
+ return status;
+}
+
+static MigrationState *migration_init_cmd(int detach, const char *command, char **argv)
+{
+ int fds[2];
+ pid_t pid;
+ int i;
+ MigrationState *s;
+
+ if (pipe(fds) == -1) {
+ term_printf("pipe() (%s)\n", strerror(errno));
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(fds[0]);
+ close(fds[1]);
+ term_printf("fork error (%s)\n", strerror(errno));
+ return NULL;
+ }
+ if (pid == 0) {
+ close(fds[1]);
+ dup2(fds[0], STDIN_FILENO);
+ execvp(command, argv);
+ exit(1);
+ } else
+ close(fds[0]);
+
+ for (i = 0; argv[i]; i++)
+ qemu_free(argv[i]);
+ qemu_free(argv);
+
+ s = migration_init_fd(detach, fds[1]);
+ if (s) {
+ MigrationCmdState *c = qemu_mallocz(sizeof(*c));
+ c->pid = pid;
+ c->fd = fds[1];
+ s->release = cmd_release;
+ s->opaque = c;
+ }
+
+ return s;
+}
+
+static MigrationState *migration_init_exec(int detach, const char *command)
+{
+ char **argv = NULL;
+
+ argv = qemu_mallocz(sizeof(char *) * 4);
+ argv[0] = strdup("sh");
+ argv[1] = strdup("-c");
+ argv[2] = strdup(command);
+ argv[3] = NULL;
+
+ return migration_init_cmd(detach, "/bin/sh", argv);
+}
+
+static MigrationState *migration_init_ssh(int detach, const char *host)
+{
+ int qemu_argc, daemonize = 0, argc, i;
+ char **qemu_argv, **argv;
+ const char *incoming = NULL;
+
+ qemu_get_launch_info(&qemu_argc, &qemu_argv, &daemonize, &incoming);
+
+ argc = 3 + qemu_argc;
+ if (!daemonize)
+ argc++;
+ if (!incoming)
+ argc+=2;
+
+ argv = qemu_mallocz(sizeof(char *) * (argc + 1));
+ argv[0] = strdup("ssh");
+ argv[1] = strdup("-XC");
+ argv[2] = strdup(host);
+
+ for (i = 0; i < qemu_argc; i++)
+ argv[3 + i] = strdup(qemu_argv[i]);
+
+ if (!daemonize)
+ argv[3 + i++] = strdup("-daemonize");
+ if (!incoming) {
+ argv[3 + i++] = strdup("-incoming");
+ argv[3 + i++] = strdup("stdio");
+ }
+
+ argv[3 + i] = NULL;
+
+ return migration_init_cmd(detach, "ssh", argv);
+}
+
+/* (busy) wait timeout (miliseconds) for a message to arrive on fd. */
+/* returns 0 on success error_code otherwise (18 for timeout) */
+static int wait_for_message(const char *msg, int fd, int timeout)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int64_t now, expiration, delta; /* milliseconds */
+ int n = 0;
+
+ now = qemu_get_clock(rt_clock);
+ expiration = now + timeout;
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ tv.tv_sec = tv.tv_usec = 0;
+ now = qemu_get_clock(rt_clock);
+ delta = expiration - now;
+ if (delta > 0)
+ tv.tv_usec = delta * 1000;
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ } while ( (n == -1) && (errno == EINTR) );
+
+ switch (n) {
+ case -1: /* error */
+ fprintf(stderr, "%s FAILED: ", msg);
+ perror("");
+ return MIG_STAT_SELECT_FAILED;
+ case 0: /* timeout */
+ fprintf(stderr, "%s: timeout reached\n", msg);
+ return MIG_STAT_SELECT_TIMEOUT;
+ case 1:
+ break;
+ default:
+ fprintf(stderr, "wait_for_message: %s: select returned %d\n", msg, n);
+ }
+ if (!FD_ISSET(fd, &rfds)) {
+ fprintf(stderr, "wait_for_message: %s: s->fd not set\n", msg);
+ return MIG_STAT_SELECT_FD_NOT_SET;
+ }
+
+ return 0;
+}
+
+static int tcp_release(void *opaque)
+{
+ MigrationState *s = opaque;
+ uint8_t status = 0;
+ ssize_t len = 0;
+ int n;
+
+ if (*s->has_error)
+ goto out;
+
+ n = wait_for_message("WAIT FOR ACK", s->fd, wait_for_message_timeout);
+ if (n) {
+ *s->has_error = n;
+ goto out;
+ }
+
+wait_for_ack:
+ len = read(s->fd, &status, 1);
+ if (len == -1 && errno == EINTR)
+ goto wait_for_ack;
+ if (len != 1 || status != 0) {
+ *s->has_error = MIG_STAT_READ_FAILED;
+ fprintf(stderr, "migration: wait_for_ack: read error l=%zu s=%d(%s)\n",
+ len, status, strerror(errno));
+ goto out;
+ }
+
+send_go:
+ len = write(s->fd, &status, 1);
+ if (len == -1 && errno == EINTR)
+ goto send_go;
+ if (len != 1) {
+ fprintf(stderr, "migration: send_go: write error l=%zu (%s)\n",
+ len, strerror(errno));
+ *s->has_error = MIG_STAT_WRITE_FAILED;
+ }
+
+out:
+ close(s->fd);
+
+ return (len != 1 || status != 0);
+}
+
+static MigrationState *migration_init_tcp(int detach, const char *host)
+{
+ int fd;
+ struct sockaddr_in addr;
+ MigrationState *s;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ term_printf("socket() failed %s\n", strerror(errno));
+ status = MIG_STAT_SOCKET_FAILED;
+ return NULL;
+ }
+
+ addr.sin_family = AF_INET;
+ if (parse_host_port(&addr, host) == -1) {
+ term_printf("parse_host_port() FAILED for %s\n", host);
+ close(fd);
+ status = MIG_STAT_INVALID_ADDR;
+ return NULL;
+ }
+
+again:
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ if (errno == EINTR)
+ goto again;
+ term_printf("connect() failed %s\n", strerror(errno));
+ close(fd);
+ status = MIG_STAT_CONNECT_FAILED;
+ return NULL;
+ }
+
+ s = migration_init_fd(detach, fd);
+ if (s) {
+ s->opaque = s;
+ s->release = tcp_release;
+ }
+ return s;
+}
+
+/* Incoming migration */
+
+static void migrate_incoming_homogeneous_page(uint32_t addr, uint32_t v)
+{
+ int i, n;
+ uint32_t *p;
+
+ n = TARGET_PAGE_SIZE / sizeof(v);
+ p = (uint32 *)(phys_ram_base + addr);
+
+ for (i=0; i<n; i++)
+ p[i] = v;
+}
+
+static int migrate_incoming_page(QEMUFile *f, uint32_t addr)
+{
+ int l, v, ret = 0;
+
+ switch (qemu_get_byte(f)) {
+ case 0: /* the whole page */
+ l = qemu_get_buffer(f, phys_ram_base + addr, TARGET_PAGE_SIZE);
+ if (l != TARGET_PAGE_SIZE)
+ ret = MIG_STAT_DST_GET_PAGE_FAILED;
+ break;
+ case 1: /* homogeneous page -- a single byte */
+ l = qemu_get_buffer(f, (void*)&v, 4);
+ if (l != 4)
+ ret = MIG_STAT_DST_GET_PAGE_FAILED;
+ migrate_incoming_homogeneous_page(addr, v);
+ break;
+ default:
+ ret = MIG_STAT_DST_GET_PAGE_UNKNOWN_TYPE;
+ }
+
+ return ret;
+}
+
+static int migrate_incoming_fd(int fd)
+{
+ int ret = 0;
+ QEMUFile *f = qemu_fopen_fd(fd);
+ uint32_t addr, size;
+ extern void qemu_announce_self(void);
+ unsigned char running;
+
+ running = qemu_get_byte(f);
+ if ((running != 1) && (running != 2)) {
+ fprintf(stderr, "migration: illegal running value %u, not (1 or 2)\n",
+ running);
+ return MIG_STAT_DST_MEM_SIZE_MISMATCH;
+ }
+ autostart = running - 1;
+
+ size = qemu_get_be32(f);
+ if (size != phys_ram_size) {
+ fprintf(stderr, "migration: memory size mismatch: recv %u mine %u\n",
+ size, phys_ram_size);
+ return MIG_STAT_DST_MEM_SIZE_MISMATCH;
+ }
+
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ int n, m;
+ unsigned char *phys_ram_page_exist_bitmap = NULL;
+
+ /* allocate memory bitmap */
+ n = BITMAP_SIZE(phys_ram_size);
+ m = qemu_get_be32(f);
+ if (m > n) // allocate enough space
+ n = m;
+ phys_ram_page_exist_bitmap = qemu_malloc(n);
+ if (!phys_ram_page_exist_bitmap) {
+ perror("failed to allocate page bitmap");
+ return MIG_STAT_NO_MEM;
+ }
+
+ /* receive memory bitmap */
+ qemu_get_buffer(f, phys_ram_page_exist_bitmap, m);
+
+ /* FIXME: free dellocated-at-src guest memory pages */
+
+ qemu_free(phys_ram_page_exist_bitmap);
+ }
+#endif
+
+ do {
+ addr = qemu_get_be32(f);
+ if (addr == 1)
+ break;
+ if (addr >= phys_ram_size)
+ return MIG_STAT_DST_MEM_OUT_OF_RANGE;
+ ret = migrate_incoming_page(f, addr);
+ if (ret)
+ return ret;
+ } while (1);
+
+
+ qemu_aio_flush();
+ vm_stop(0);
+ if (qemu_live_loadvm_state(f))
+ ret = MIG_STAT_DST_LOADVM_FAILED;
+#ifdef MIGRATION_VERIFY
+ if (ret==0) ret=load_verify_memory(f, NULL, 1);
+#endif /* MIGRATION_VERIFY */
+ qemu_fclose(f);
+
+ return ret;
+}
+
+static int migrate_incoming_tcp(const char *host)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int fd, sfd;
+ ssize_t len;
+ uint8_t status = 0;
+ int reuse = 1;
+ int rc;
+
+ addr.sin_family = AF_INET;
+ if (parse_host_port(&addr, host) == -1) {
+ fprintf(stderr, "parse_host_port() failed for %s\n", host);
+ rc = MIG_STAT_DST_INVALID_ADDR;
+ goto error;
+ }
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd == -1) {
+ perror("socket failed");
+ rc = MIG_STAT_DST_SOCKET_FAILED;
+ goto error;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
+ perror("setsockopt() failed");
+ rc = MIG_STAT_DST_SOCKOPT_FAILED;
+ goto error_socket;
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("bind() failed");
+ rc = MIG_STAT_DST_BIND_FAILED;
+ goto error_socket;
+ }
+
+ if (listen(fd, 1) == -1) {
+ perror("listen() failed");
+ rc = MIG_STAT_DST_LISTEN_FAILED;
+ goto error_socket;
+ }
+
+again:
+ sfd = accept(fd, (struct sockaddr *)&addr, &addrlen);
+ if (sfd == -1) {
+ if (errno == EINTR)
+ goto again;
+ perror("accept() failed");
+ rc = MIG_STAT_DST_ACCEPT_FAILED;
+ goto error_socket;
+ }
+
+ rc = migrate_incoming_fd(sfd);
+ if (rc != 0) {
+ fprintf(stderr, "migrate_incoming_fd failed (rc=%d)\n", rc);
+ goto error_accept;
+ }
+
+send_ack:
+ len = write(sfd, &status, 1);
+ if (len == -1 && errno == EAGAIN)
+ goto send_ack;
+ if (len != 1) {
+ fprintf(stderr, "migration: send_ack: write error len=%zu (%s)\n",
+ len, strerror(errno));
+ rc = MIG_STAT_DST_WRITE_FAILED;
+ goto error_accept;
+ }
+
+ rc = wait_for_message("WAIT FOR GO", sfd, wait_for_message_timeout);
+ if (rc) {
+ rc += 200;
+ goto error_accept;
+ }
+
+wait_for_go:
+ len = read(sfd, &status, 1);
+ if (len == -1 && errno == EAGAIN)
+ goto wait_for_go;
+ if (len != 1) {
+ rc = MIG_STAT_DST_READ_FAILED;
+ fprintf(stderr, "migration: wait_for_go: read error len=%zu (%s)\n",
+ len, strerror(errno));
+ }
+
+error_accept:
+ close(sfd);
+error_socket:
+ close(fd);
+error:
+ return rc;
+}
+
+int migrate_incoming(const char *device)
+{
+ const char *ptr;
+ int ret = 0;
+
+ if (strcmp(device, "stdio") == 0)
+ ret = migrate_incoming_fd(STDIN_FILENO);
+ else if (strstart(device, "file://", &ptr)) {
+ int fd = open(ptr, O_RDONLY);
+ if (fd < 0) {
+ ret = MIG_STAT_DST_INVALID_PARAMS;
+ } else {
+ ret = migrate_incoming_fd(fd);
+ close(fd);
+ }
+ } else if (strstart(device, "tcp://", &ptr)) {
+ char *host, *end;
+ host = strdup(ptr);
+ end = strchr(host, '/');
+ if (end) *end = 0;
+ ret = migrate_incoming_tcp(host);
+ qemu_free(host);
+ } else {
+ errno = EINVAL;
+ ret = MIG_STAT_DST_INVALID_PARAMS;
+ }
+
+ return ret;
+}
+
+/* Migration monitor command */
+
+/* TODO:
+ 1) audit all error paths
+*/
+
+void do_migrate(int detach, const char *uri)
+{
+ const char *ptr;
+ MigrationState *s = current_migration;
+
+ if (s) {
+ term_printf("Migration already active\n");
+ return;
+ }
+
+ status = MIG_STAT_INVALID_PARAMS;
+ if (strstart(uri, "exec:", &ptr)) {
+ char *command = urldecode(ptr);
+ migration_init_exec(detach, command);
+ free(command);
+ } else if (strstart(uri, "ssh://", &ptr)) {
+ char *host, *end;
+
+ host = strdup(ptr);
+ end = strchr(host, '/');
+ if (end) *end = 0;
+ migration_init_ssh(detach, host);
+ qemu_free(host);
+ } else if (strstart(uri, "tcp://", &ptr)) {
+ char *host, *end;
+
+ host = strdup(ptr);
+ end = strchr(host, '/');
+ if (end) *end = 0;
+
+ if (migration_init_tcp(detach, host) == NULL)
+ term_printf("migration failed (migration_init_tcp for %s failed)\n", host);
+ free(host);
+ } else {
+ term_printf("Unknown migration protocol '%s'\n", uri);
+ return;
+ }
+}
+
+void do_migrate_set_speed(const char *value)
+{
+ double d;
+ char *ptr;
+
+ d = strtod(value, &ptr);
+ switch (*ptr) {
+ case 'G': case 'g':
+ d *= 1024;
+ case 'M': case 'm':
+ d *= 1024;
+ case 'K': case 'k':
+ d *= 1024;
+ default:
+ break;
+ }
+
+ max_throttle = (uint32_t)d;
+}
+
+void do_info_migration(void)
+{
+ MigrationState *s = current_migration;
+
+ if (s) {
+ term_printf("Migration active\n");
+ if (s->bps < (1 << 20))
+ term_printf("Transfer rate %3.1f kb/s\n",
+ (double)s->bps / 1024);
+ else
+ term_printf("Transfer rate %3.1f mb/s\n",
+ (double)s->bps / (1024 * 1024));
+ term_printf("Iteration %d\n", s->iteration);
+ term_printf("Transferred %d/%d pages\n", s->updated_pages, phys_ram_size >> TARGET_PAGE_BITS);
+ if (s->iteration)
+ term_printf("Last iteration found %d dirty pages\n", s->last_updated_pages);
+ } else {
+ term_printf("Migration inactive\n");
+ term_printf("last migration status is %d\n", status);
+ }
+ term_printf("Maximum migration speed is ");
+ if (max_throttle < (1 << 20))
+ term_printf("%3.1f kb/s\n", (double)max_throttle / 1024);
+ else
+ term_printf("%3.1f mb/s\n", (double)max_throttle / (1024 * 1024));
+}
+
+void do_migrate_cancel(void)
+{
+ MigrationState *s = current_migration;
+
+ if (s)
+ *s->has_error = MIG_STAT_MIGRATION_CANCEL;
+}
+
+
+
+#ifdef MIGRATION_VERIFY
+unsigned int calc_page_checksum(target_ulong addr)
+{
+ unsigned int sum=0;
+ unsigned int *p = (unsigned int *)(phys_ram_base + addr);
+ unsigned int *q = p + (TARGET_PAGE_SIZE / sizeof(unsigned int));
+
+ for ( /*initialized already */ ; p<q ; p++)
+ sum += *p;
+ return sum;
+}
+
+
+static int save_verify_memory(QEMUFile *f, void *opaque)
+{
+ unsigned int addr;
+ unsigned int sum;
+
+ for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (addr>=0xa0000) && (addr<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
+ sum = calc_page_checksum(addr);
+ qemu_put_be32(f, addr);
+ qemu_put_be32(f, sum);
+ }
+ return 0;
+}
+
+static int load_verify_memory(QEMUFile *f, void *opaque, int version_id)
+{
+ unsigned int addr, raddr;
+ unsigned int sum, rsum;
+ int num_errors = 0;
+
+ for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (addr>=0xa0000) && (addr<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
+ sum = calc_page_checksum(addr);
+ raddr = qemu_get_be32(f);
+ rsum = qemu_get_be32(f);
+ if ((raddr != addr) || (rsum != sum)) {
+ term_printf("checksum mismatch: src:0x%x 0x%x , dst:0x%x 0x%x\n",
+ raddr, rsum, addr, sum);
+ num_errors++;
+ }
+ }
+ printf("memory_verify: num_errors=%d\n", num_errors);
+ term_printf("memory_verify: num_errors=%d\n", num_errors);
+ return 0/* num_errors */;
+}
+#endif /* MIGRATION_VERIFY */
diff --git a/migration.h b/migration.h
new file mode 100644
index 000000000..06a2e4134
--- /dev/null
+++ b/migration.h
@@ -0,0 +1,10 @@
+#ifndef MIGRATION_H
+#define MIGRATION_H
+
+void do_info_migration(void);
+void do_migrate(int detach, const char *uri);
+void do_migrate_cancel(void);
+void do_migrate_set_speed(const char *value);
+int migrate_incoming(const char *device);
+
+#endif
diff --git a/monitor.c b/monitor.c
index 97ac583e1..417d2546a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -34,8 +34,10 @@
#include "block.h"
#include "audio/audio.h"
#include "disas.h"
+#include "migration.h"
#include <dirent.h>
+#include "qemu-kvm.h"
#ifdef CONFIG_PROFILER
#include "qemu-timer.h" /* for ticks_per_sec */
#endif
@@ -283,6 +285,11 @@ static CPUState *mon_get_cpu(void)
if (!mon_cpu) {
mon_set_cpu(0);
}
+
+#ifdef USE_KVM
+ kvm_save_registers(mon_cpu);
+#endif
+
return mon_cpu;
}
@@ -1326,6 +1333,12 @@ static term_cmd_t term_cmds[] = {
"capture index", "stop capture" },
{ "memsave", "lis", do_memory_save,
"addr size file", "save to disk virtual memory dump starting at 'addr' of size 'size'", },
+ { "migrate", "-ds", do_migrate,
+ "[-d] command", "migrate the VM using command (use -d to not wait for command to complete)" },
+ { "migrate_cancel", "", do_migrate_cancel,
+ "", "cancel the current VM migration" },
+ { "migrate_set_speed", "s", do_migrate_set_speed,
+ "value", "set maximum speed (in bytes) for migrations" },
{ NULL, NULL, },
};
@@ -1386,6 +1399,8 @@ static term_cmd_t info_cmds[] = {
{ "slirp", "", do_info_slirp,
"", "show SLIRP statistics", },
#endif
+ { "migration", "", do_info_migration,
+ "", "show migration information" },
{ NULL, NULL, },
};
@@ -2543,12 +2558,26 @@ static void term_read(void *opaque, const uint8_t *buf, int size)
readline_handle_byte(buf[i]);
}
+static int monitor_suspended;
+
+void monitor_suspend(void)
+{
+ monitor_suspended = 1;
+}
+
+void monitor_resume(void)
+{
+ monitor_suspended = 0;
+ monitor_start_input();
+}
+
static void monitor_start_input(void);
static void monitor_handle_command1(void *opaque, const char *cmdline)
{
monitor_handle_command(cmdline);
- monitor_start_input();
+ if (!monitor_suspended)
+ monitor_start_input();
}
static void monitor_start_input(void)
diff --git a/osdep.c b/osdep.c
index 00c19ce6e..aa83b6d2b 100644
--- a/osdep.c
+++ b/osdep.c
@@ -181,7 +181,7 @@ void *qemu_vmalloc(size_t size)
#ifdef _BSD
return valloc(size);
#else
- return memalign(4096, size);
+ return memalign(TARGET_PAGE_SIZE, size);
#endif
}
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
index 54e0bec80..745543fb2 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/bios.diff b/pc-bios/bios.diff
index 660d6d334..2bdfd5ca4 100644
--- a/pc-bios/bios.diff
+++ b/pc-bios/bios.diff
@@ -1,13 +1,38 @@
+? _rombios_.c
+? _rombiosl_.c
+? a.out
+? biossums
+? rombios.s
+? rombios.sym
+? rombios.txt
+? rombios16.bin
+? rombios32.bin
+? rombios32.out
+? rombiosl.s
+? rombiosl.sym
+? rombiosl.txt
+Index: BIOS-bochs-latest
+===================================================================
+RCS file: /cvsroot/bochs/bochs/bios/BIOS-bochs-latest,v
+retrieving revision 1.145
+diff -u -r1.145 BIOS-bochs-latest
+Binary files /tmp/cvsHcT9RD and BIOS-bochs-latest differ
+Index: BIOS-bochs-legacy
+===================================================================
+RCS file: /cvsroot/bochs/bochs/bios/BIOS-bochs-legacy,v
+retrieving revision 1.9
+diff -u -r1.9 BIOS-bochs-legacy
+Binary files /tmp/cvszbAyyn and BIOS-bochs-legacy differ
Index: rombios.c
===================================================================
RCS file: /cvsroot/bochs/bochs/bios/rombios.c,v
-retrieving revision 1.174
-diff -u -w -r1.174 rombios.c
---- rombios.c 17 Oct 2006 16:48:05 -0000 1.174
-+++ rombios.c 8 Feb 2007 21:57:48 -0000
-@@ -9472,6 +9472,14 @@
- mov eax, #0x00040000
- call eax
+retrieving revision 1.178
+diff -u -r1.178 rombios.c
+--- rombios.c 2 Apr 2007 20:35:08 -0000 1.178
++++ rombios.c 17 May 2007 13:32:36 -0000
+@@ -9521,6 +9521,14 @@
+ rep
+ stosd
+ ;; reset the memory (some boot loaders such as syslinux suppose
+ ;; that the memory is set to zero)
@@ -23,10 +48,10 @@ diff -u -w -r1.174 rombios.c
Index: rombios.h
===================================================================
RCS file: /cvsroot/bochs/bochs/bios/rombios.h,v
-retrieving revision 1.3
-diff -u -w -r1.3 rombios.h
---- rombios.h 3 Oct 2006 20:27:30 -0000 1.3
-+++ rombios.h 8 Feb 2007 21:57:48 -0000
+retrieving revision 1.4
+diff -u -r1.4 rombios.h
+--- rombios.h 20 Feb 2007 09:36:55 -0000 1.4
++++ rombios.h 17 May 2007 13:32:36 -0000
@@ -19,7 +19,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
@@ -39,11 +64,11 @@ diff -u -w -r1.3 rombios.h
Index: rombios32.c
===================================================================
RCS file: /cvsroot/bochs/bochs/bios/rombios32.c,v
-retrieving revision 1.8
-diff -u -w -r1.8 rombios32.c
---- rombios32.c 3 Oct 2006 20:27:30 -0000 1.8
-+++ rombios32.c 8 Feb 2007 21:57:48 -0000
-@@ -852,6 +852,11 @@
+retrieving revision 1.9
+diff -u -r1.9 rombios32.c
+--- rombios32.c 20 Feb 2007 09:36:55 -0000 1.9
++++ rombios32.c 17 May 2007 13:32:40 -0000
+@@ -855,6 +855,11 @@
int ioapic_id, i, len;
int mp_config_table_size;
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
new file mode 100644
index 000000000..7a729aa81
--- /dev/null
+++ b/pc-bios/openbios-sparc
Binary files differ
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
index 34f9a9ff7..c129b71c0 100644
--- a/pc-bios/vgabios-cirrus.bin
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
index 17cb63f6a..1e0e5e1f8 100644
--- a/pc-bios/vgabios.bin
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/qemu-common.h b/qemu-common.h
index 2c955219d..a383f4fb5 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -81,6 +81,8 @@ char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int stristart(const char *str, const char *val, const char **ptr);
time_t mktimegm(struct tm *tm);
+int hex2bin(char ch);
+char *urldecode(const char *ptr);
/* Error handling. */
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..0029a43f0
--- /dev/null
+++ b/qemu-kvm-ia64.c
@@ -0,0 +1,71 @@
+
+#include "config.h"
+#include "config-host.h"
+
+extern int kvm_allowed;
+extern int kvm_irqchip;
+
+#ifdef USE_KVM
+
+#include <string.h>
+#include "vl.h"
+
+#include "qemu-kvm.h"
+#include <libkvm.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+
+
+extern kvm_context_t kvm_context;
+
+extern __thread CPUState *vcpu_env;
+
+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, int vcpu)
+{
+ CPUState *env = cpu_single_env;
+ env->hflags |= HF_HALTED_MASK;
+ env->exception_index = EXCP_HLT;
+ return 1;
+}
+
+void kvm_arch_pre_kvm_run(void *opaque, int vcpu)
+{
+}
+
+void kvm_arch_post_kvm_run(void *opaque, int vcpu)
+{
+}
+
+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)
+{
+}
+#endif
diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c
new file mode 100644
index 000000000..d86fec30e
--- /dev/null
+++ b/qemu-kvm-x86.c
@@ -0,0 +1,628 @@
+
+#include "config.h"
+#include "config-host.h"
+
+extern int kvm_allowed;
+extern int kvm_irqchip;
+
+#ifdef USE_KVM
+
+#include <string.h>
+#include "hw/hw.h"
+
+#include "qemu-kvm.h"
+#include <libkvm.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+
+#define MSR_IA32_TSC 0x10
+
+static struct kvm_msr_list *kvm_msr_list;
+extern unsigned int kvm_shadow_memory;
+extern kvm_context_t kvm_context;
+static int kvm_has_msr_star;
+
+static int lm_capable_kernel;
+extern __thread CPUState *vcpu_env;
+
+int kvm_arch_qemu_create_context(void)
+{
+ int i;
+ 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;
+ 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;
+ 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);
+}
+
+/* the reset values of qemu are not compatible to SVM
+ * this function is used to fix the segment descriptor values */
+static void fix_realmode_dataseg(struct kvm_segment *seg)
+{
+ seg->type = 0x02;
+ seg->present = 1;
+ seg->s = 1;
+}
+
+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(kvm_context, env->cpu_index, &regs);
+
+ 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(kvm_context, env->cpu_index, &fpu);
+
+ memcpy(sregs.interrupt_bitmap, env->kvm_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;
+ }
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ fix_realmode_dataseg(&sregs.cs);
+ fix_realmode_dataseg(&sregs.ds);
+ fix_realmode_dataseg(&sregs.es);
+ fix_realmode_dataseg(&sregs.fs);
+ fix_realmode_dataseg(&sregs.gs);
+ fix_realmode_dataseg(&sregs.ss);
+ }
+ }
+
+ 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.apic_base = cpu_get_apic_base(env);
+ sregs.efer = env->efer;
+ sregs.cr8 = cpu_get_apic_tpr(env);
+
+ kvm_set_sregs(kvm_context, env->cpu_index, &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);
+ set_msr_entry(&msrs[n++], MSR_IA32_TSC, env->tsc);
+#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(kvm_context, env->cpu_index, msrs, n);
+ if (rc == -1)
+ perror("kvm_set_msrs FAILED");
+}
+
+
+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(kvm_context, env->cpu_index, &regs);
+
+ 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(kvm_context, env->cpu_index, &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(kvm_context, env->cpu_index, &sregs);
+
+ memcpy(env->kvm_interrupt_bitmap, sregs.interrupt_bitmap, sizeof(env->kvm_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;
+ env->cc_src = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ env->df = 1 - (2 * ((env->eflags >> 10) & 1));
+ env->cc_op = CC_OP_EFLAGS;
+ env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+
+ /* 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;
+#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(kvm_context, env->cpu_index, 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 host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx)
+{
+ uint32_t vec[4];
+
+ vec[0] = function;
+ asm volatile (
+#ifdef __x86_64__
+ "sub $128, %%rsp \n\t" /* skip red zone */
+ "push %0; push %%rsi \n\t"
+ "push %%rax; push %%rbx; push %%rcx; push %%rdx \n\t"
+ "mov 8*5(%%rsp), %%rsi \n\t"
+ "mov (%%rsi), %%eax \n\t"
+ "cpuid \n\t"
+ "mov %%eax, (%%rsi) \n\t"
+ "mov %%ebx, 4(%%rsi) \n\t"
+ "mov %%ecx, 8(%%rsi) \n\t"
+ "mov %%edx, 12(%%rsi) \n\t"
+ "pop %%rdx; pop %%rcx; pop %%rbx; pop %%rax \n\t"
+ "pop %%rsi; pop %0 \n\t"
+ "add $128, %%rsp"
+#else
+ "push %0; push %%esi \n\t"
+ "push %%eax; push %%ebx; push %%ecx; push %%edx \n\t"
+ "mov 4*5(%%esp), %%esi \n\t"
+ "mov (%%esi), %%eax \n\t"
+ "cpuid \n\t"
+ "mov %%eax, (%%esi) \n\t"
+ "mov %%ebx, 4(%%esi) \n\t"
+ "mov %%ecx, 8(%%esi) \n\t"
+ "mov %%edx, 12(%%esi) \n\t"
+ "pop %%edx; pop %%ecx; pop %%ebx; pop %%eax \n\t"
+ "pop %%esi; pop %0 \n\t"
+#endif
+ : : "rm"(vec) : "memory");
+ if (eax)
+ *eax = vec[0];
+ if (ebx)
+ *ebx = vec[1];
+ if (ecx)
+ *ecx = vec[2];
+ if (edx)
+ *edx = vec[3];
+}
+
+
+static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
+ CPUState *env)
+{
+ env->regs[R_EAX] = function;
+ qemu_kvm_cpuid_on_env(env);
+ e->function = function;
+ e->eax = env->regs[R_EAX];
+ e->ebx = env->regs[R_EBX];
+ e->ecx = env->regs[R_ECX];
+ e->edx = env->regs[R_EDX];
+ if (function == 0x80000001) {
+ uint32_t h_eax, h_edx;
+ struct utsname utsname;
+
+ host_cpuid(function, &h_eax, NULL, NULL, &h_edx);
+ uname(&utsname);
+ lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
+
+ // long mode
+ if ((h_edx & 0x20000000) == 0 || !lm_capable_kernel)
+ e->edx &= ~0x20000000u;
+ // syscall
+ if ((h_edx & 0x00000800) == 0)
+ e->edx &= ~0x00000800u;
+ // nx
+ if ((h_edx & 0x00100000) == 0)
+ e->edx &= ~0x00100000u;
+ // svm
+ if (e->ecx & 4)
+ e->ecx &= ~4u;
+ }
+ // sysenter isn't supported on compatibility mode on AMD. and syscall
+ // isn't supported in compatibility mode on Intel. so advertise the
+ // actuall cpu, and say goodbye to migration between different vendors
+ // is you use compatibility mode.
+ if (function == 0) {
+ uint32_t bcd[3];
+
+ host_cpuid(0, NULL, &bcd[0], &bcd[1], &bcd[2]);
+ e->ebx = bcd[0];
+ e->ecx = bcd[1];
+ e->edx = bcd[2];
+ }
+}
+
+int kvm_arch_qemu_init_env(CPUState *cenv)
+{
+ struct kvm_cpuid_entry cpuid_ent[100];
+#ifdef KVM_CPUID_SIGNATURE
+ struct kvm_cpuid_entry *pv_ent;
+ uint32_t signature[3];
+#endif
+ int cpuid_nent = 0;
+ CPUState copy;
+ uint32_t i, limit;
+
+ copy = *cenv;
+
+#ifdef KVM_CPUID_SIGNATURE
+ /* Paravirtualization CPUIDs */
+ memcpy(signature, "KVMKVMKVM", 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 = 0;
+#endif
+
+ copy.regs[R_EAX] = 0;
+ qemu_kvm_cpuid_on_env(&copy);
+ limit = copy.regs[R_EAX];
+
+ for (i = 0; i <= limit; ++i)
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, &copy);
+
+ copy.regs[R_EAX] = 0x80000000;
+ qemu_kvm_cpuid_on_env(&copy);
+ limit = copy.regs[R_EAX];
+
+ for (i = 0x80000000; i <= limit; ++i)
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, &copy);
+
+ kvm_setup_cpuid(kvm_context, cenv->cpu_index, cpuid_nent, cpuid_ent);
+ return 0;
+}
+
+int kvm_arch_halt(void *opaque, int vcpu)
+{
+ CPUState *env = cpu_single_env;
+
+ if (!((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK))) {
+ env->hflags |= HF_HALTED_MASK;
+ env->exception_index = EXCP_HLT;
+ }
+ return 1;
+}
+
+void kvm_arch_pre_kvm_run(void *opaque, int vcpu)
+{
+ CPUState *env = cpu_single_env;
+
+ if (!kvm_irqchip_in_kernel(kvm_context))
+ kvm_set_cr8(kvm_context, vcpu, cpu_get_apic_tpr(env));
+}
+
+void kvm_arch_post_kvm_run(void *opaque, int vcpu)
+{
+ CPUState *env = vcpu_env;
+ cpu_single_env = env;
+
+ env->eflags = kvm_get_interrupt_flag(kvm_context, vcpu)
+ ? env->eflags | IF_MASK : env->eflags & ~IF_MASK;
+ env->ready_for_interrupt_injection
+ = kvm_is_ready_for_interrupt_injection(kvm_context, vcpu);
+
+ cpu_set_apic_tpr(env, kvm_get_cr8(kvm_context, vcpu));
+ cpu_set_apic_base(env, kvm_get_apic_base(kvm_context, vcpu));
+}
+
+int kvm_arch_has_work(CPUState *env)
+{
+ if ((env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXIT)) &&
+ (env->eflags & IF_MASK))
+ return 1;
+ return 0;
+}
+
+int kvm_arch_try_push_interrupts(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r, irq;
+
+ if (env->ready_for_interrupt_injection &&
+ (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(kvm_context, env->cpu_index, irq);
+ if (r < 0)
+ printf("cpu %d fail inject %x\n", env->cpu_index, irq);
+ }
+ }
+
+ return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0;
+}
+
+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);
+}
+#endif
diff --git a/qemu-kvm.c b/qemu-kvm.c
new file mode 100644
index 000000000..8c48b2913
--- /dev/null
+++ b/qemu-kvm.c
@@ -0,0 +1,807 @@
+
+#include "config.h"
+#include "config-host.h"
+
+#ifdef USE_KVM
+ #define KVM_ALLOWED_DEFAULT 1
+#else
+ #define KVM_ALLOWED_DEFAULT 0
+#endif
+
+int kvm_allowed = KVM_ALLOWED_DEFAULT;
+int kvm_irqchip = 1;
+
+#ifdef USE_KVM
+
+#include <string.h>
+#include "hw/hw.h"
+#include "sysemu.h"
+
+#include "qemu-kvm.h"
+#include <libkvm.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+
+extern void perror(const char *s);
+
+kvm_context_t kvm_context;
+
+extern int smp_cpus;
+
+pthread_mutex_t qemu_mutex = PTHREAD_MUTEX_INITIALIZER;
+__thread CPUState *vcpu_env;
+
+static sigset_t io_sigset, io_negsigset;
+
+static int wait_hack;
+
+#define SIG_IPI (SIGRTMIN+4)
+
+struct vcpu_info {
+ int sipi_needed;
+ int init;
+ pthread_t thread;
+ int signalled;
+ int stop;
+ int stopped;
+} vcpu_info[4];
+
+static void sig_ipi_handler(int n)
+{
+}
+
+void kvm_update_interrupt_request(CPUState *env)
+{
+ if (env && env != vcpu_env) {
+ if (vcpu_info[env->cpu_index].signalled)
+ return;
+ vcpu_info[env->cpu_index].signalled = 1;
+ if (vcpu_info[env->cpu_index].thread)
+ pthread_kill(vcpu_info[env->cpu_index].thread, SIG_IPI);
+ }
+}
+
+void kvm_update_after_sipi(CPUState *env)
+{
+ vcpu_info[env->cpu_index].sipi_needed = 1;
+ kvm_update_interrupt_request(env);
+
+ /*
+ * the qemu bios waits using a busy loop that's much too short for
+ * kvm. add a wait after the first sipi.
+ */
+ {
+ static int first_sipi = 1;
+
+ if (first_sipi) {
+ wait_hack = 1;
+ first_sipi = 0;
+ }
+ }
+}
+
+void kvm_apic_init(CPUState *env)
+{
+ if (env->cpu_index != 0)
+ vcpu_info[env->cpu_index].init = 1;
+ kvm_update_interrupt_request(env);
+}
+
+#include <signal.h>
+
+static int try_push_interrupts(void *opaque)
+{
+ return kvm_arch_try_push_interrupts(opaque);
+}
+
+static void post_kvm_run(void *opaque, int vcpu)
+{
+
+ pthread_mutex_lock(&qemu_mutex);
+ kvm_arch_post_kvm_run(opaque, vcpu);
+}
+
+static int pre_kvm_run(void *opaque, int vcpu)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env->cpu_index == 0 && wait_hack) {
+ int i;
+
+ wait_hack = 0;
+
+ pthread_mutex_unlock(&qemu_mutex);
+ for (i = 0; i < 10; ++i)
+ usleep(1000);
+ pthread_mutex_lock(&qemu_mutex);
+ }
+
+ kvm_arch_pre_kvm_run(opaque, vcpu);
+
+ if (env->interrupt_request & CPU_INTERRUPT_EXIT)
+ return 1;
+ pthread_mutex_unlock(&qemu_mutex);
+ return 0;
+}
+
+void kvm_load_registers(CPUState *env)
+{
+ if (kvm_allowed)
+ kvm_arch_load_regs(env);
+}
+
+void kvm_save_registers(CPUState *env)
+{
+ if (kvm_allowed)
+ kvm_arch_save_regs(env);
+}
+
+int kvm_cpu_exec(CPUState *env)
+{
+ int r;
+
+ r = kvm_run(kvm_context, env->cpu_index);
+ if (r < 0) {
+ printf("kvm_run returned %d\n", r);
+ exit(1);
+ }
+
+ return 0;
+}
+
+extern int vm_running;
+
+static int has_work(CPUState *env)
+{
+ if (!vm_running)
+ return 0;
+ if (!(env->hflags & HF_HALTED_MASK))
+ return 1;
+ return kvm_arch_has_work(env);
+}
+
+static int kvm_eat_signal(CPUState *env, int timeout)
+{
+ struct timespec ts;
+ int r, e, ret = 0;
+ siginfo_t siginfo;
+ struct sigaction sa;
+
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+ r = sigtimedwait(&io_sigset, &siginfo, &ts);
+ if (r == -1 && (errno == EAGAIN || errno == EINTR) && !timeout)
+ return 0;
+ e = errno;
+ pthread_mutex_lock(&qemu_mutex);
+ cpu_single_env = vcpu_env;
+ if (r == -1 && !(errno == EAGAIN || errno == EINTR)) {
+ printf("sigtimedwait: %s\n", strerror(e));
+ exit(1);
+ }
+ if (r != -1) {
+ sigaction(siginfo.si_signo, NULL, &sa);
+ sa.sa_handler(siginfo.si_signo);
+ ret = 1;
+ }
+ pthread_mutex_unlock(&qemu_mutex);
+
+ return ret;
+}
+
+
+static void kvm_eat_signals(CPUState *env, int timeout)
+{
+ int r = 0;
+
+ while (kvm_eat_signal(env, 0))
+ r = 1;
+ if (!r && timeout) {
+ r = kvm_eat_signal(env, timeout);
+ if (r)
+ while (kvm_eat_signal(env, 0))
+ ;
+ }
+ /*
+ * we call select() even if no signal was received, to account for
+ * for which there is no signal handler installed.
+ */
+ pthread_mutex_lock(&qemu_mutex);
+ cpu_single_env = vcpu_env;
+ main_loop_wait(0);
+ pthread_mutex_unlock(&qemu_mutex);
+}
+
+static void kvm_main_loop_wait(CPUState *env, int timeout)
+{
+ pthread_mutex_unlock(&qemu_mutex);
+ if (env->cpu_index == 0)
+ kvm_eat_signals(env, timeout);
+ else {
+ if (!kvm_irqchip_in_kernel(kvm_context) &&
+ (timeout || vcpu_info[env->cpu_index].stopped)) {
+ sigset_t set;
+ int n;
+
+ paused:
+ sigemptyset(&set);
+ sigaddset(&set, SIG_IPI);
+ sigwait(&set, &n);
+ } else {
+ struct timespec ts;
+ siginfo_t siginfo;
+ sigset_t set;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ sigemptyset(&set);
+ sigaddset(&set, SIG_IPI);
+ sigtimedwait(&set, &siginfo, &ts);
+ }
+ if (vcpu_info[env->cpu_index].stop) {
+ vcpu_info[env->cpu_index].stop = 0;
+ vcpu_info[env->cpu_index].stopped = 1;
+ pthread_kill(vcpu_info[0].thread, SIG_IPI);
+ goto paused;
+ }
+ }
+ pthread_mutex_lock(&qemu_mutex);
+ cpu_single_env = env;
+ vcpu_info[env->cpu_index].signalled = 0;
+}
+
+static int all_threads_paused(void)
+{
+ int i;
+
+ for (i = 1; i < smp_cpus; ++i)
+ if (vcpu_info[i].stopped)
+ return 0;
+ return 1;
+}
+
+static void pause_other_threads(void)
+{
+ int i;
+
+ for (i = 1; i < smp_cpus; ++i) {
+ vcpu_info[i].stop = 1;
+ pthread_kill(vcpu_info[i].thread, SIG_IPI);
+ }
+ while (!all_threads_paused())
+ kvm_eat_signals(vcpu_env, 0);
+}
+
+static void resume_other_threads(void)
+{
+ int i;
+
+ for (i = 1; i < smp_cpus; ++i) {
+ vcpu_info[i].stop = 0;
+ vcpu_info[i].stopped = 0;
+ pthread_kill(vcpu_info[i].thread, SIG_IPI);
+ }
+}
+
+static void kvm_vm_state_change_handler(void *context, int running)
+{
+ if (running)
+ resume_other_threads();
+ else
+ pause_other_threads();
+}
+
+static void update_regs_for_sipi(CPUState *env)
+{
+ kvm_arch_update_regs_for_sipi(env);
+ vcpu_info[env->cpu_index].sipi_needed = 0;
+ vcpu_info[env->cpu_index].init = 0;
+}
+
+static void update_regs_for_init(CPUState *env)
+{
+ cpu_reset(env);
+ kvm_arch_load_regs(env);
+}
+
+static void setup_kernel_sigmask(CPUState *env)
+{
+ sigset_t set;
+
+ sigprocmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+ if (env->cpu_index == 0)
+ sigandset(&set, &set, &io_negsigset);
+
+ kvm_set_signal_mask(kvm_context, env->cpu_index, &set);
+}
+
+static int kvm_main_loop_cpu(CPUState *env)
+{
+ struct vcpu_info *info = &vcpu_info[env->cpu_index];
+
+ setup_kernel_sigmask(env);
+ pthread_mutex_lock(&qemu_mutex);
+
+ kvm_qemu_init_env(env);
+ env->ready_for_interrupt_injection = 1;
+
+ cpu_single_env = env;
+ while (1) {
+ while (!has_work(env))
+ kvm_main_loop_wait(env, 10);
+ if (env->interrupt_request & CPU_INTERRUPT_HARD)
+ env->hflags &= ~HF_HALTED_MASK;
+ if (!kvm_irqchip_in_kernel(kvm_context) && info->sipi_needed)
+ update_regs_for_sipi(env);
+ if (!kvm_irqchip_in_kernel(kvm_context) && info->init)
+ update_regs_for_init(env);
+ if (!(env->hflags & HF_HALTED_MASK) && !info->init)
+ kvm_cpu_exec(env);
+ env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
+ kvm_main_loop_wait(env, 0);
+ if (qemu_shutdown_requested())
+ break;
+ else if (qemu_powerdown_requested())
+ qemu_system_powerdown();
+ else if (qemu_reset_requested()) {
+ env->interrupt_request = 0;
+ qemu_system_reset();
+ kvm_arch_load_regs(env);
+ }
+ }
+ pthread_mutex_unlock(&qemu_mutex);
+ return 0;
+}
+
+static void *ap_main_loop(void *_env)
+{
+ CPUState *env = _env;
+ sigset_t signals;
+
+ vcpu_env = env;
+ sigfillset(&signals);
+ //sigdelset(&signals, SIG_IPI);
+ sigprocmask(SIG_BLOCK, &signals, NULL);
+ kvm_create_vcpu(kvm_context, env->cpu_index);
+ kvm_qemu_init_env(env);
+ if (kvm_irqchip_in_kernel(kvm_context))
+ env->hflags &= ~HF_HALTED_MASK;
+ kvm_main_loop_cpu(env);
+ return NULL;
+}
+
+static void kvm_add_signal(int signum)
+{
+ sigaddset(&io_sigset, signum);
+ sigdelset(&io_negsigset, signum);
+ sigprocmask(SIG_BLOCK, &io_sigset, NULL);
+}
+
+int kvm_init_ap(void)
+{
+ CPUState *env = first_cpu->next_cpu;
+ int i;
+
+ qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL);
+ sigemptyset(&io_sigset);
+ sigfillset(&io_negsigset);
+ kvm_add_signal(SIGIO);
+ kvm_add_signal(SIGALRM);
+ kvm_add_signal(SIGUSR2);
+ if (!kvm_irqchip_in_kernel(kvm_context))
+ kvm_add_signal(SIG_IPI);
+
+ vcpu_env = first_cpu;
+ signal(SIG_IPI, sig_ipi_handler);
+ for (i = 1; i < smp_cpus; ++i) {
+ pthread_create(&vcpu_info[i].thread, NULL, ap_main_loop, env);
+ env = env->next_cpu;
+ }
+ return 0;
+}
+
+int kvm_main_loop(void)
+{
+ vcpu_info[0].thread = pthread_self();
+ return kvm_main_loop_cpu(first_cpu);
+}
+
+static int kvm_debug(void *opaque, int vcpu)
+{
+ CPUState *env = cpu_single_env;
+
+ env->exception_index = EXCP_DEBUG;
+ return 1;
+}
+
+static int kvm_inb(void *opaque, uint16_t addr, uint8_t *data)
+{
+ *data = cpu_inb(0, addr);
+ return 0;
+}
+
+static int kvm_inw(void *opaque, uint16_t addr, uint16_t *data)
+{
+ *data = cpu_inw(0, addr);
+ return 0;
+}
+
+static int kvm_inl(void *opaque, uint16_t addr, uint32_t *data)
+{
+ *data = cpu_inl(0, addr);
+ return 0;
+}
+
+#define PM_IO_BASE 0xb000
+
+static int kvm_outb(void *opaque, uint16_t addr, uint8_t data)
+{
+ if (addr == 0xb2) {
+ switch (data) {
+ case 0: {
+ cpu_outb(0, 0xb3, 0);
+ break;
+ }
+ case 0xf0: {
+ unsigned x;
+
+ /* enable acpi */
+ x = cpu_inw(0, PM_IO_BASE + 4);
+ x &= ~1;
+ cpu_outw(0, PM_IO_BASE + 4, x);
+ break;
+ }
+ case 0xf1: {
+ unsigned x;
+
+ /* enable acpi */
+ x = cpu_inw(0, PM_IO_BASE + 4);
+ x |= 1;
+ cpu_outw(0, PM_IO_BASE + 4, x);
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+ }
+ cpu_outb(0, addr, data);
+ return 0;
+}
+
+static int kvm_outw(void *opaque, uint16_t addr, uint16_t data)
+{
+ cpu_outw(0, addr, data);
+ return 0;
+}
+
+static int kvm_outl(void *opaque, uint16_t addr, uint32_t data)
+{
+ cpu_outl(0, addr, data);
+ return 0;
+}
+
+static int kvm_readb(void *opaque, uint64_t addr, uint8_t *data)
+{
+ *data = ldub_phys(addr);
+ return 0;
+}
+
+static int kvm_readw(void *opaque, uint64_t addr, uint16_t *data)
+{
+ *data = lduw_phys(addr);
+ return 0;
+}
+
+static int kvm_readl(void *opaque, uint64_t addr, uint32_t *data)
+{
+ /* hack: Red Hat 7.1 generates some wierd accesses. */
+ if (addr > 0xa0000 - 4 && addr < 0xa0000) {
+ *data = 0;
+ return 0;
+ }
+
+ *data = ldl_phys(addr);
+ return 0;
+}
+
+static int kvm_readq(void *opaque, uint64_t addr, uint64_t *data)
+{
+ *data = ldq_phys(addr);
+ return 0;
+}
+
+static int kvm_writeb(void *opaque, uint64_t addr, uint8_t data)
+{
+ stb_phys(addr, data);
+ return 0;
+}
+
+static int kvm_writew(void *opaque, uint64_t addr, uint16_t data)
+{
+ stw_phys(addr, data);
+ return 0;
+}
+
+static int kvm_writel(void *opaque, uint64_t addr, uint32_t data)
+{
+ stl_phys(addr, data);
+ return 0;
+}
+
+static int kvm_writeq(void *opaque, uint64_t addr, uint64_t data)
+{
+ stq_phys(addr, data);
+ return 0;
+}
+
+static int kvm_io_window(void *opaque)
+{
+ return 1;
+}
+
+
+static int kvm_halt(void *opaque, int vcpu)
+{
+ return kvm_arch_halt(opaque, vcpu);
+}
+
+static int kvm_shutdown(void *opaque, int vcpu)
+{
+ qemu_system_reset_request();
+ return 1;
+}
+
+static struct kvm_callbacks qemu_kvm_ops = {
+ .debug = kvm_debug,
+ .inb = kvm_inb,
+ .inw = kvm_inw,
+ .inl = kvm_inl,
+ .outb = kvm_outb,
+ .outw = kvm_outw,
+ .outl = kvm_outl,
+ .readb = kvm_readb,
+ .readw = kvm_readw,
+ .readl = kvm_readl,
+ .readq = kvm_readq,
+ .writeb = kvm_writeb,
+ .writew = kvm_writew,
+ .writel = kvm_writel,
+ .writeq = kvm_writeq,
+ .halt = kvm_halt,
+ .shutdown = kvm_shutdown,
+ .io_window = kvm_io_window,
+ .try_push_interrupts = try_push_interrupts,
+ .post_kvm_run = post_kvm_run,
+ .pre_kvm_run = pre_kvm_run,
+};
+
+int kvm_qemu_init()
+{
+ /* Try to initialize kvm */
+ kvm_context = kvm_init(&qemu_kvm_ops, cpu_single_env);
+ if (!kvm_context) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int kvm_qemu_create_context(void)
+{
+ int r;
+ if (!kvm_irqchip) {
+ kvm_disable_irqchip_creation(kvm_context);
+ }
+ if (kvm_create(kvm_context, phys_ram_size, (void**)&phys_ram_base) < 0) {
+ kvm_qemu_destroy();
+ return -1;
+ }
+ r = kvm_arch_qemu_create_context();
+ if(r <0)
+ kvm_qemu_destroy();
+ return 0;
+}
+
+void kvm_qemu_destroy(void)
+{
+ kvm_finalize(kvm_context);
+}
+
+void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr,
+ unsigned long size,
+ unsigned long phys_offset)
+{
+#ifdef KVM_CAP_USER_MEMORY
+ int r = 0;
+
+ r = kvm_check_extension(kvm_context, KVM_CAP_USER_MEMORY);
+ if (r) {
+ if (!(phys_offset & ~TARGET_PAGE_MASK)) {
+ r = kvm_is_allocated_mem(kvm_context, start_addr, size);
+ if (r)
+ return;
+ r = kvm_is_intersecting_mem(kvm_context, start_addr);
+ if (r)
+ kvm_create_mem_hole(kvm_context, start_addr, size);
+ r = kvm_register_userspace_phys_mem(kvm_context, start_addr,
+ phys_ram_base + phys_offset,
+ size, 0);
+ }
+ if (phys_offset & IO_MEM_ROM) {
+ phys_offset &= ~IO_MEM_ROM;
+ r = kvm_is_intersecting_mem(kvm_context, start_addr);
+ if (r)
+ kvm_create_mem_hole(kvm_context, start_addr, size);
+ r = kvm_register_userspace_phys_mem(kvm_context, start_addr,
+ phys_ram_base + phys_offset,
+ size, 0);
+ }
+ if (r < 0) {
+ printf("kvm_cpu_register_physical_memory: failed\n");
+ exit(1);
+ }
+ return;
+ }
+#endif
+ if (phys_offset & IO_MEM_ROM) {
+ phys_offset &= ~IO_MEM_ROM;
+ memcpy(phys_ram_base + start_addr, phys_ram_base + phys_offset, size);
+ }
+}
+
+int kvm_qemu_check_extension(int ext)
+{
+ return kvm_check_extension(kvm_context, ext);
+}
+
+int kvm_qemu_init_env(CPUState *cenv)
+{
+ return kvm_arch_qemu_init_env(cenv);
+}
+
+int kvm_update_debugger(CPUState *env)
+{
+ struct kvm_debug_guest dbg;
+ int i;
+
+ dbg.enabled = 0;
+ if (env->nb_breakpoints || env->singlestep_enabled) {
+ dbg.enabled = 1;
+ for (i = 0; i < 4 && i < env->nb_breakpoints; ++i) {
+ dbg.breakpoints[i].enabled = 1;
+ dbg.breakpoints[i].address = env->breakpoints[i];
+ }
+ dbg.singlestep = env->singlestep_enabled;
+ }
+ return kvm_guest_debug(kvm_context, env->cpu_index, &dbg);
+}
+
+
+/*
+ * 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_allowed)
+ 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 */
+int kvm_get_dirty_pages_log_range(unsigned long start_addr,
+ unsigned char *bitmap,
+ unsigned int offset,
+ unsigned long mem_size)
+{
+ unsigned int i, j, n=0;
+ unsigned char c;
+ unsigned page_number, addr, addr1;
+ 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;
+ cpu_physical_memory_set_dirty(addr);
+ n++;
+ }
+ }
+ return 0;
+}
+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, phys_ram_size,
+ kvm_dirty_bitmap, NULL,
+ kvm_get_dirty_bitmap_cb);
+ return r;
+}
+
+int kvm_get_phys_ram_bitmap_cb(unsigned long start, unsigned long len,
+ void *local_bitmap, void *qemu_bitmap)
+{
+ unsigned int bsize = ((len/TARGET_PAGE_SIZE) + 7) / 8;
+ unsigned int offset = ((start/TARGET_PAGE_SIZE) + 7) / 8;
+
+ memcpy(qemu_bitmap + offset, local_bitmap, bsize);
+
+ return 0;
+}
+
+int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap)
+{
+ int r=0;
+ void *local_bitmap;
+ unsigned int bsize = BITMAP_SIZE(phys_ram_size);
+
+ local_bitmap = qemu_malloc(bsize);
+ if (!local_bitmap) {
+ fprintf(stderr, "could not allocate memory for phys_page bitmap\n");
+ return 1;
+ }
+
+ r = kvm_get_mem_map_range(kvm_context, 0, phys_ram_size,
+ local_bitmap, bitmap,
+ kvm_get_phys_ram_bitmap_cb);
+
+ qemu_free(local_bitmap);
+ return r;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_set_irq(int irq, int level)
+{
+ return kvm_set_irq_level(kvm_context, irq, level);
+}
+
+#endif
+
+#endif
diff --git a/qemu-kvm.h b/qemu-kvm.h
new file mode 100644
index 000000000..9f4572862
--- /dev/null
+++ b/qemu-kvm.h
@@ -0,0 +1,48 @@
+#ifndef QEMU_KVM_H
+#define QEMU_KVM_H
+
+#include "cpu.h"
+#include "libkvm.h"
+
+int kvm_main_loop(void);
+int kvm_qemu_init(void);
+int kvm_qemu_create_context(void);
+int kvm_init_ap(void);
+void kvm_qemu_destroy(void);
+void kvm_load_registers(CPUState *env);
+void kvm_save_registers(CPUState *env);
+int kvm_cpu_exec(CPUState *env);
+int kvm_update_debugger(CPUState *env);
+int kvm_qemu_init_env(CPUState *env);
+int kvm_qemu_check_extension(int ext);
+void kvm_apic_init(CPUState *env);
+
+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_update_after_sipi(CPUState *env);
+void kvm_update_interrupt_request(CPUState *env);
+void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr,
+ unsigned long size,
+ unsigned long phys_offset);
+int kvm_arch_qemu_create_context(void);
+
+void kvm_arch_save_regs(CPUState *env);
+void kvm_arch_load_regs(CPUState *env);
+int kvm_arch_qemu_init_env(CPUState *cenv);
+int kvm_arch_halt(void *opaque, int vcpu);
+void kvm_arch_pre_kvm_run(void *opaque, int vcpu);
+void kvm_arch_post_kvm_run(void *opaque, int vcpu);
+int kvm_arch_has_work(CPUState *env);
+int kvm_arch_try_push_interrupts(void *opaque);
+void kvm_arch_update_regs_for_sipi(CPUState *env);
+
+extern int kvm_allowed;
+extern int kvm_irqchip;
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+#define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8)
+#endif
diff --git a/qemu_socket.h b/qemu_socket.h
index 6e8bcce6c..6687de724 100644
--- a/qemu_socket.h
+++ b/qemu_socket.h
@@ -28,4 +28,6 @@
void socket_set_nonblock(int fd);
+int parse_host_port(struct sockaddr_in *saddr, const char *str);
+
#endif /* QEMU_SOCKET_H */
diff --git a/sdl.c b/sdl.c
index edd173e06..5b9a06b81 100644
--- a/sdl.c
+++ b/sdl.c
@@ -89,7 +89,7 @@ static void sdl_resize(DisplayState *ds, int w, int h)
ds->data = screen->pixels;
ds->linesize = screen->pitch;
ds->depth = screen->format->BitsPerPixel;
- if (screen->format->Bshift > screen->format->Rshift) {
+ if (ds->depth == 32 && screen->format->Rshift == 0) {
ds->bgr = 1;
} else {
ds->bgr = 0;
@@ -220,7 +220,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
static void sdl_update_caption(void)
{
- char buf[1024];
+ char buf[1024], appname[20] = "QEMU";
const char *status = "";
if (!vm_running)
@@ -232,12 +232,18 @@ static void sdl_update_caption(void)
status = " - Press Ctrl-Alt-Shift to exit grab";
}
+#if USE_KVM
+ if (kvm_allowed) {
+ strncat(appname, "/KVM", sizeof(appname));
+ }
+#endif
+
if (qemu_name)
- snprintf(buf, sizeof(buf), "QEMU (%s)%s", qemu_name, status);
+ snprintf(buf, sizeof(buf), "%s (%s)%s", appname, qemu_name, status);
else
- snprintf(buf, sizeof(buf), "QEMU%s", status);
+ snprintf(buf, sizeof(buf), "%s%s", appname, status);
- SDL_WM_SetCaption(buf, "QEMU");
+ SDL_WM_SetCaption(buf, appname);
}
static void sdl_hide_cursor(void)
diff --git a/sysemu.h b/sysemu.h
index 8bb88b12c..1f5b093b6 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -69,7 +69,7 @@ int tap_win32_init(VLANState *vlan, const char *ifname);
/* SLIRP */
void do_info_slirp(void);
-extern int ram_size;
+extern int64_t ram_size;
extern int bios_size;
extern int rtc_utc;
extern int rtc_start_date;
@@ -137,6 +137,10 @@ DriveInfo drives_table[MAX_DRIVES+1];
extern int drive_get_index(BlockInterfaceType interface, int bus, int unit);
extern int drive_get_max_bus(BlockInterfaceType interface);
+/* vmchannel devices */
+
+#define MAX_VMCHANNEL_DEVICES 4
+
/* serial ports */
#define MAX_SERIAL_PORTS 4
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 2114cba92..7143ab36a 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -160,14 +160,19 @@
#define HF_MP_MASK (1 << HF_MP_SHIFT)
#define HF_EM_MASK (1 << HF_EM_SHIFT)
#define HF_TS_MASK (1 << HF_TS_SHIFT)
+#define HF_IOPL_MASK (3 << HF_IOPL_SHIFT)
#define HF_LMA_MASK (1 << HF_LMA_SHIFT)
#define HF_CS64_MASK (1 << HF_CS64_SHIFT)
#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT)
+#define HF_VM_MASK (1 << HF_VM_SHIFT)
#define HF_HALTED_MASK (1 << HF_HALTED_SHIFT)
#define HF_SMM_MASK (1 << HF_SMM_SHIFT)
#define HF_GIF_MASK (1 << HF_GIF_SHIFT)
#define HF_HIF_MASK (1 << HF_HIF_SHIFT)
+#define CR0_PE_SHIFT 0
+#define CR0_MP_SHIFT 1
+
#define CR0_PE_MASK (1 << 0)
#define CR0_MP_MASK (1 << 1)
#define CR0_EM_MASK (1 << 2)
@@ -186,7 +191,8 @@
#define CR4_PAE_MASK (1 << 5)
#define CR4_PGE_MASK (1 << 7)
#define CR4_PCE_MASK (1 << 8)
-#define CR4_OSFXSR_MASK (1 << 9)
+#define CR4_OSFXSR_SHIFT 9
+#define CR4_OSFXSR_MASK (1 << CR4_OSFXSR_SHIFT)
#define CR4_OSXMMEXCPT_MASK (1 << 10)
#define PG_PRESENT_BIT 0
@@ -549,6 +555,10 @@ typedef struct CPUX86State {
target_ulong kernelgsbase;
#endif
+#ifdef USE_KVM
+ uint64_t tsc; /* time stamp counter */
+ uint8_t ready_for_interrupt_injection;
+#endif
uint64_t pat;
/* exception/interrupt handling */
@@ -583,6 +593,13 @@ typedef struct CPUX86State {
int kqemu_enabled;
int last_io_time;
#endif
+
+#ifdef USE_KVM
+#define BITS_PER_LONG (8 * sizeof (uint32_t))
+#define NR_IRQ_WORDS (256/ BITS_PER_LONG)
+ uint32_t kvm_interrupt_bitmap[NR_IRQ_WORDS];
+#endif
+
/* in order to simplify APIC support, we leave this pointer to the
user */
struct APICState *apic_state;
diff --git a/target-i386/helper2.c b/target-i386/helper2.c
index 6d40c641f..1874b73ba 100644
--- a/target-i386/helper2.c
+++ b/target-i386/helper2.c
@@ -29,6 +29,10 @@
#include "exec-all.h"
#include "svm.h"
+#ifdef USE_KVM
+#include "../qemu-kvm.h"
+#endif
+
//#define DEBUG_MMU
static int cpu_x86_register (CPUX86State *env, const char *cpu_model);
@@ -90,6 +94,8 @@ static void add_flagname_to_bitmaps(char *flagname, uint32_t *features,
fprintf(stderr, "CPU feature %s not found\n", flagname);
}
+extern const char *cpu_vendor_string;
+
CPUX86State *cpu_x86_init(const char *cpu_model)
{
CPUX86State *env;
@@ -317,6 +323,8 @@ static int cpu_x86_register (CPUX86State *env, const char *cpu_model)
{
const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
int c, len, i;
+ if (cpu_vendor_string != NULL)
+ model_id = cpu_vendor_string;
len = strlen(model_id);
for(i = 0; i < 48; i++) {
if (i >= len)
@@ -354,9 +362,9 @@ void cpu_reset(CPUX86State *env)
env->idt.limit = 0xffff;
env->gdt.limit = 0xffff;
env->ldt.limit = 0xffff;
- env->ldt.flags = DESC_P_MASK;
+ env->ldt.flags = DESC_P_MASK | (2 << DESC_TYPE_SHIFT);
env->tr.limit = 0xffff;
- env->tr.flags = DESC_P_MASK;
+ env->tr.flags = DESC_P_MASK | (11 << DESC_TYPE_SHIFT);
cpu_x86_load_seg_cache(env, R_CS, 0xf000, 0xffff0000, 0xffff, 0);
cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffff, 0);
diff --git a/target-ia64/cpu.h b/target-ia64/cpu.h
new file mode 100644
index 000000000..7349e94b4
--- /dev/null
+++ b/target-ia64/cpu.h
@@ -0,0 +1,81 @@
+/*
+ * 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>
+#include<stdio.h>
+
+#define TARGET_LONG_BITS 64
+
+
+#define TARGET_PAGE_BITS 14
+
+#define ELF_MACHINE EM_IA_64
+
+#define CPU_PAL_HALT 1
+#define HF_HALTED_MASK (1 << CPU_PAL_HALT)
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+typedef struct CPUIA64State {
+ CPU_COMMON;
+ /* exception/interrupt handling */
+ jmp_buf jmp_env;
+ int exception_index;
+
+ int interrupt_request;
+ int user_mode_only;
+ uint32_t hflags;
+
+#ifdef USE_KVM
+ uint8_t ready_for_interrupt_injection;
+#endif
+
+} CPUIA64State;
+
+#define CPUState CPUIA64State
+#define cpu_gen_code cpu_ia64_gen_code
+#define cpu_init cpu_ia64_init
+#define cpu_signal_handler cpu_ia64_signal_handler
+
+struct CPUIA64State *env;
+int cpu_get_pic_interrupt(CPUIA64State *s);
+int cpu_exec(CPUState *env1);
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags);
+
+CPUState *cpu_ia64_init(void);
+
+#include "cpu-all.h"
+
+/* IA64 has seperate I/D cache, with coherence maintained by DMA controller.
+ * So to emulate right behavior that guest OS is assumed, we need to flush
+ * I/D cache here.
+ */
+void kvm_sync_icache(unsigned long address, int len);
+#endif
diff --git a/target-ia64/exec.h b/target-ia64/exec.h
new file mode 100644
index 000000000..155cfa67a
--- /dev/null
+++ b/target-ia64/exec.h
@@ -0,0 +1,46 @@
+/*
+ * 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
+ */
+//#include "dyngen-exec.h"
+
+#include "cpu.h"
+#include "exec-all.h"
+uint32_t T0;
+uint32_t T1;
+uint32_t T2;
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+void cpu_lock(void);
+void cpu_unlock(void);
+void cpu_loop_exit(void);
+
+static inline int cpu_halted(CPUState *env) {
+ /* handle exit of HALTED state */
+ if (!(env->hflags & HF_HALTED_MASK))
+ return 0;
+ return EXCP_HALTED;
+}
diff --git a/target-ia64/firmware.c b/target-ia64/firmware.c
new file mode 100644
index 000000000..7b1a65dad
--- /dev/null
+++ b/target-ia64/firmware.c
@@ -0,0 +1,579 @@
+/*
+ * 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 <zlib.h>
+#include "cpu.h"
+
+#include "firmware.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_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 build_hob(void* hob_buf, unsigned long hob_buf_size,
+ unsigned long dom_mem_size, unsigned long vcpus);
+static int load_hob(void *hob_buf,
+ unsigned long dom_mem_size, void* hob_start);
+
+int
+kvm_ia64_build_hob(unsigned long memsize,
+ unsigned long vcpus, uint8_t* fw_start)
+{
+ 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) < 0) {
+ free(hob_buf);
+ Hob_Output("Could not build hob");
+ return -1;
+ }
+ if (load_hob(hob_buf, memsize, fw_start + HOB_OFFSET) < 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
+build_hob(void* hob_buf, unsigned long hob_buf_size,
+ unsigned long dom_mem_size, unsigned long vcpus)
+{
+ //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;
+ }
+
+ return 0;
+
+err_out:
+ return -1;
+}
+static int
+load_hob(void *hob_buf,
+ unsigned long dom_mem_size, void* hob_start)
+{
+ 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;
+ }
+ memcpy (hob_start, hob_buf, hob_size);
+ return 0;
+}
+
+#define MIN(x, y) ((x) < (y)) ? (x) : (y)
+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 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;
+}
+char *read_image(const char *filename, unsigned long *size)
+{
+ int kernel_fd = -1;
+ gzFile kernel_gfd = NULL;
+ char *image = NULL, *tmp;
+ unsigned int bytes;
+
+ if ( (filename == NULL) || (size == NULL) )
+ return NULL;
+
+ if ( (kernel_fd = open(filename, O_RDONLY)) < 0 )
+ {
+ Hob_Output("Could not open kernel image");
+ goto out;
+ }
+
+ if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
+ {
+ Hob_Output("Could not allocate decompression state for state file");
+ goto out;
+ }
+
+ *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;
+}
+
+/*
+ * 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..1c4e534c2
--- /dev/null
+++ b/target-ia64/firmware.h
@@ -0,0 +1,46 @@
+/*
+ * 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, strcat("HOB:",s))
+
+extern int kvm_ia64_build_hob(unsigned long memsize,
+ unsigned long vcpus, uint8_t* fw_start);
+extern char *read_image(const char *filename, unsigned long *size);
+
+#endif //__FIRM_WARE_
+
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..1b8226a64
--- /dev/null
+++ b/target-ia64/op_helper.c
@@ -0,0 +1,109 @@
+/*
+ * 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"
+
+extern int kvm_allowed;
+
+CPUState *cpu_ia64_init(void){
+ CPUState *env;
+ env = qemu_mallocz(sizeof(CPUState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ cpu_reset(env);
+#ifdef USE_KVM
+ {
+ if (kvm_allowed) {
+ kvm_qemu_init_env(env);
+ env->ready_for_interrupt_injection = 1;
+ }
+ }
+#endif
+ 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)
+{
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ printf("%s: unexpect\n", __FUNCTION__);
+ exit(-1);
+ }
+#endif
+}
+
+
+
+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/vl.c b/vl.c
index 70950f844..28805a6cd 100644
--- a/vl.c
+++ b/vl.c
@@ -37,6 +37,7 @@
#include "qemu-char.h"
#include "block.h"
#include "audio/audio.h"
+#include "qemu-kvm.h"
#include <unistd.h>
#include <fcntl.h>
@@ -131,6 +132,10 @@ int inet_aton(const char *cp, struct in_addr *ia);
#include "exec-all.h"
+#if USE_KVM
+#include "qemu-kvm.h"
+#endif
+
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
#ifdef __sun__
@@ -142,7 +147,11 @@ int inet_aton(const char *cp, struct in_addr *ia);
//#define DEBUG_UNUSED_IOPORT
//#define DEBUG_IOPORT
+#if HOST_LONG_BITS < 64
#define PHYS_RAM_MAX_SIZE (2047 * 1024 * 1024)
+#else
+#define PHYS_RAM_MAX_SIZE (2047 * 1024 * 1024 * 1024ULL)
+#endif
#ifdef TARGET_PPC
#define DEFAULT_RAM_SIZE 144
@@ -174,7 +183,7 @@ static DisplayState display_state;
int nographic;
const char* keyboard_layout = NULL;
int64_t ticks_per_sec;
-int ram_size;
+int64_t ram_size;
int pit_min_timer_count = 0;
int nb_nics;
NICInfo nd_table[MAX_NICS];
@@ -195,6 +204,8 @@ int graphic_depth = 15;
int full_screen = 0;
int no_frame = 0;
int no_quit = 0;
+int balloon_used = 0;
+CharDriverState *vmchannel_hds[MAX_VMCHANNEL_DEVICES];
CharDriverState *serial_hds[MAX_SERIAL_PORTS];
CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
#ifdef TARGET_I386
@@ -217,10 +228,14 @@ int no_reboot = 0;
int cursor_hide = 1;
int graphic_rotate = 0;
int daemonize = 0;
+const char *incoming;
const char *option_rom[MAX_OPTION_ROMS];
int nb_option_roms;
int semihosting_enabled = 0;
int autostart = 1;
+int time_drift_fix = 0;
+unsigned int kvm_shadow_memory = 0;
+const char *cpu_vendor_string;
#ifdef TARGET_ARM
int old_param = 0;
#endif
@@ -822,7 +837,6 @@ struct qemu_alarm_timer {
};
#define ALARM_FLAG_DYNTICKS 0x1
-#define ALARM_FLAG_MODIFIED 0x2
static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
{
@@ -834,11 +848,6 @@ static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
if (!alarm_has_dynticks(t))
return;
- if (!(t->flags & ALARM_FLAG_MODIFIED))
- return;
-
- t->flags &= ~(ALARM_FLAG_MODIFIED);
-
t->rearm(t);
}
@@ -1001,8 +1010,6 @@ void qemu_del_timer(QEMUTimer *ts)
{
QEMUTimer **pt, *t;
- alarm_timer->flags |= ALARM_FLAG_MODIFIED;
-
/* NOTE: this code must be signal safe because
qemu_timer_expired() can be called from a signal. */
pt = &active_timers[ts->clock->type];
@@ -1075,6 +1082,7 @@ static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time)
/* run the callback (the timer list can be modified) */
ts->cb(ts->opaque);
}
+ qemu_rearm_alarm_timer(alarm_timer);
}
int64_t qemu_get_clock(QEMUClock *clock)
@@ -1182,7 +1190,8 @@ static void host_alarm_handler(int host_signum)
last_clock = ti;
}
#endif
- if (alarm_has_dynticks(alarm_timer) ||
+ if (1 ||
+ alarm_has_dynticks(alarm_timer) ||
qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
qemu_get_clock(vm_clock)) ||
qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
@@ -1194,7 +1203,6 @@ static void host_alarm_handler(int host_signum)
CPUState *env = next_cpu;
if (env) {
- alarm_timer->flags |= ALARM_FLAG_MODIFIED;
/* stop the currently executing cpu because a timer occured */
cpu_interrupt(env, CPU_INTERRUPT_EXIT);
#ifdef USE_KQEMU
@@ -2949,7 +2957,6 @@ static void udp_chr_update_read_handler(CharDriverState *chr)
}
}
-int parse_host_port(struct sockaddr_in *saddr, const char *str);
#ifndef _WIN32
static int parse_unix_path(struct sockaddr_un *uaddr, const char *str);
#endif
@@ -3910,6 +3917,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
if (!s)
return NULL;
s->fd = fd;
+ enable_sigio_timer(fd);
s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
qemu_set_fd_handler(s->fd, tap_send, NULL, s);
snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
@@ -5511,17 +5519,56 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
}
#endif
+#define SELF_ANNOUNCE_ROUNDS 5
+#define ETH_P_EXPERIMENTAL 0x01F1 /* just a number */
+//#define ETH_P_EXPERIMENTAL 0x0012 /* make it the size of the packet */
+#define EXPERIMENTAL_MAGIC 0xf1f23f4f
+
+static int announce_self_create(uint8_t *buf,
+ uint8_t *mac_addr)
+{
+ uint32_t magic = EXPERIMENTAL_MAGIC;
+ uint16_t proto = htons(ETH_P_EXPERIMENTAL);
+
+ /* FIXME: should we send a different packet (arp/rarp/ping)? */
+
+ memset(buf, 0xff, 6); /* h_dst */
+ memcpy(buf + 6, mac_addr, 6); /* h_src */
+ memcpy(buf + 12, &proto, 2); /* h_proto */
+ memcpy(buf + 14, &magic, 4); /* magic */
+
+ return 18; /* len */
+}
+
+static void qemu_announce_self(void)
+{
+ int i, j, len;
+ VLANState *vlan;
+ VLANClientState *vc;
+ uint8_t buf[256];
+
+ for (i = 0; i < nb_nics; i++) {
+ len = announce_self_create(buf, nd_table[i].macaddr);
+ vlan = nd_table[i].vlan;
+ for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+ if (vc->fd_read == tap_receive) /* send only if tap */
+ for (j=0; j < SELF_ANNOUNCE_ROUNDS; j++)
+ vc->fd_read(vc->opaque, buf, len);
+ }
+ }
+}
+
/***********************************************************/
/* savevm/loadvm support */
#define IO_BUF_SIZE 32768
struct QEMUFile {
- FILE *outfile;
- BlockDriverState *bs;
- int is_file;
- int is_writable;
- int64_t base_offset;
+ QEMUFilePutBufferFunc *put_buffer;
+ QEMUFileGetBufferFunc *get_buffer;
+ QEMUFileCloseFunc *close;
+ void *opaque;
+
int64_t buf_offset; /* start of buffer when writing, end of buffer
when reading */
int buf_index;
@@ -5529,58 +5576,143 @@ struct QEMUFile {
uint8_t buf[IO_BUF_SIZE];
};
-QEMUFile *qemu_fopen(const char *filename, const char *mode)
+typedef struct QEMUFileFD
{
- QEMUFile *f;
+ int fd;
+} QEMUFileFD;
- f = qemu_mallocz(sizeof(QEMUFile));
- if (!f)
- return NULL;
- if (!strcmp(mode, "wb")) {
- f->is_writable = 1;
- } else if (!strcmp(mode, "rb")) {
- f->is_writable = 0;
- } else {
- goto fail;
+static int fd_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileFD *s = opaque;
+ int offset = 0;
+ ssize_t len;
+
+again:
+ len = read(s->fd, buf + offset, size - offset);
+ if (len == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ goto again;
}
- f->outfile = fopen(filename, mode);
- if (!f->outfile)
+
+ return len;
+}
+
+QEMUFile *qemu_fopen_fd(int fd)
+{
+ QEMUFileFD *s = qemu_mallocz(sizeof(QEMUFileFD));
+ s->fd = fd;
+ return qemu_fopen(s, NULL, fd_get_buffer, qemu_free);
+}
+
+typedef struct QEMUFileUnix
+{
+ FILE *outfile;
+} QEMUFileUnix;
+
+static void file_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileUnix *s = opaque;
+ fseek(s->outfile, pos, SEEK_SET);
+ fwrite(buf, 1, size, s->outfile);
+}
+
+static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileUnix *s = opaque;
+ fseek(s->outfile, pos, SEEK_SET);
+ return fread(buf, 1, size, s->outfile);
+}
+
+static void file_close(void *opaque)
+{
+ QEMUFileUnix *s = opaque;
+ fclose(s->outfile);
+ qemu_free(s);
+}
+
+QEMUFile *qemu_fopen_file(const char *filename, const char *mode)
+{
+ QEMUFileUnix *s;
+
+ s = qemu_mallocz(sizeof(QEMUFileUnix));
+ if (!s)
+ return NULL;
+
+ s->outfile = fopen(filename, mode);
+ if (!s->outfile)
goto fail;
- f->is_file = 1;
- return f;
- fail:
- if (f->outfile)
- fclose(f->outfile);
- qemu_free(f);
+
+ if (!strcmp(mode, "wb"))
+ return qemu_fopen(s, file_put_buffer, NULL, file_close);
+ else if (!strcmp(mode, "rb"))
+ return qemu_fopen(s, NULL, file_get_buffer, file_close);
+
+fail:
+ if (s->outfile)
+ fclose(s->outfile);
+ qemu_free(s);
return NULL;
}
-static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable)
+typedef struct QEMUFileBdrv
+{
+ BlockDriverState *bs;
+ int64_t base_offset;
+} QEMUFileBdrv;
+
+static void bdrv_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileBdrv *s = opaque;
+ bdrv_pwrite(s->bs, s->base_offset + pos, buf, size);
+}
+
+static int bdrv_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileBdrv *s = opaque;
+ return bdrv_pread(s->bs, s->base_offset + pos, buf, size);
+}
+
+QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable)
+{
+ QEMUFileBdrv *s;
+
+ s = qemu_mallocz(sizeof(QEMUFileBdrv));
+ if (!s)
+ return NULL;
+
+ s->bs = bs;
+ s->base_offset = offset;
+
+ if (is_writable)
+ return qemu_fopen(s, bdrv_put_buffer, NULL, qemu_free);
+
+ return qemu_fopen(s, NULL, bdrv_get_buffer, qemu_free);
+}
+
+QEMUFile *qemu_fopen(void *opaque, QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer, QEMUFileCloseFunc *close)
{
QEMUFile *f;
f = qemu_mallocz(sizeof(QEMUFile));
if (!f)
- return NULL;
- f->is_file = 0;
- f->bs = bs;
- f->is_writable = is_writable;
- f->base_offset = offset;
+ return NULL;
+
+ f->opaque = opaque;
+ f->put_buffer = put_buffer;
+ f->get_buffer = get_buffer;
+ f->close = close;
+
return f;
}
void qemu_fflush(QEMUFile *f)
{
- if (!f->is_writable)
+ if (!f->put_buffer)
return;
+
if (f->buf_index > 0) {
- if (f->is_file) {
- fseek(f->outfile, f->buf_offset, SEEK_SET);
- fwrite(f->buf, 1, f->buf_index, f->outfile);
- } else {
- bdrv_pwrite(f->bs, f->base_offset + f->buf_offset,
- f->buf, f->buf_index);
- }
+ f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
f->buf_offset += f->buf_index;
f->buf_index = 0;
}
@@ -5590,19 +5722,13 @@ static void qemu_fill_buffer(QEMUFile *f)
{
int len;
- if (f->is_writable)
+ if (!f->get_buffer)
return;
- if (f->is_file) {
- fseek(f->outfile, f->buf_offset, SEEK_SET);
- len = fread(f->buf, 1, IO_BUF_SIZE, f->outfile);
- if (len < 0)
- len = 0;
- } else {
- len = bdrv_pread(f->bs, f->base_offset + f->buf_offset,
- f->buf, IO_BUF_SIZE);
- if (len < 0)
- len = 0;
- }
+
+ len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+ if (len < 0)
+ len = 0;
+
f->buf_index = 0;
f->buf_size = len;
f->buf_offset += len;
@@ -5610,11 +5736,9 @@ static void qemu_fill_buffer(QEMUFile *f)
void qemu_fclose(QEMUFile *f)
{
- if (f->is_writable)
- qemu_fflush(f);
- if (f->is_file) {
- fclose(f->outfile);
- }
+ qemu_fflush(f);
+ if (f->close)
+ f->close(f->opaque);
qemu_free(f);
}
@@ -5689,7 +5813,7 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
/* SEEK_END not supported */
return -1;
}
- if (f->is_writable) {
+ if (f->put_buffer) {
qemu_fflush(f);
f->buf_offset = pos;
} else {
@@ -5884,12 +6008,90 @@ static int qemu_loadvm_state(QEMUFile *f)
if (ret < 0) {
fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
instance_id, idstr);
+ goto the_end;
}
}
/* always seek to exact end of record */
qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+ }
+ ret = 0;
+ the_end:
+ return ret;
+}
+
+int qemu_live_savevm_state(QEMUFile *f)
+{
+ SaveStateEntry *se;
+ int len, ret;
+
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+
+ for(se = first_se; se != NULL; se = se->next) {
+ len = strlen(se->idstr);
+
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, se->idstr, len);
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ se->save_state(f, se->opaque);
+ }
+
+ qemu_put_byte(f, 0);
+
+ ret = 0;
+ return ret;
+}
+
+int qemu_live_loadvm_state(QEMUFile *f)
+{
+ SaveStateEntry *se;
+ int len, ret, instance_id, version_id;
+ unsigned int v;
+ char idstr[256];
+
+ v = qemu_get_be32(f);
+ if (v != QEMU_VM_FILE_MAGIC)
+ goto fail;
+ v = qemu_get_be32(f);
+ if (v != QEMU_VM_FILE_VERSION) {
+ fail:
+ ret = -1;
+ goto the_end;
+ }
+
+ for(;;) {
+ len = qemu_get_byte(f);
+ if (len == 0)
+ break;
+ qemu_get_buffer(f, idstr, len);
+ idstr[len] = '\0';
+ instance_id = qemu_get_be32(f);
+ version_id = qemu_get_be32(f);
+ se = find_se(idstr, instance_id);
+ if (!se) {
+ fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n",
+ instance_id, idstr);
+ } else {
+ if (version_id > se->version_id) { /* src version > dst version */
+ fprintf(stderr, "migration:version mismatch:%s:%d(s)>%d(d)\n",
+ idstr, version_id, se->version_id);
+ ret = -1;
+ goto the_end;
+ }
+ ret = se->load_state(f, se->opaque, version_id);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
+ instance_id, idstr);
+ goto the_end;
+ }
+ }
}
ret = 0;
+
+ qemu_announce_self();
+
the_end:
return ret;
}
@@ -6210,6 +6412,11 @@ void cpu_save(QEMUFile *f, void *opaque)
uint32_t hflags;
int i;
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_save_registers(env);
+#endif
+
for(i = 0; i < CPU_NB_REGS; i++)
qemu_put_betls(f, &env->regs[i]);
qemu_put_betls(f, &env->eip);
@@ -6294,6 +6501,16 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be64s(f, &env->kernelgsbase);
#endif
qemu_put_be32s(f, &env->smbase);
+
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ for (i = 0; i < NR_IRQ_WORDS ; i++) {
+ qemu_put_be32s(f, &env->kvm_interrupt_bitmap[i]);
+ }
+ qemu_put_be64s(f, &env->tsc);
+ }
+#endif
+
}
#ifdef USE_X86LDOUBLE
@@ -6436,6 +6653,18 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
/* XXX: compute hflags from scratch, except for CPL and IIF */
env->hflags = hflags;
tlb_flush(env, 1);
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ /* when in-kernel irqchip is used, HF_HALTED_MASK causes deadlock
+ because no userspace IRQs will ever clear this flag */
+ env->hflags &= ~HF_HALTED_MASK;
+ for (i = 0; i < NR_IRQ_WORDS ; i++) {
+ qemu_get_be32s(f, &env->kvm_interrupt_bitmap[i]);
+ }
+ qemu_get_be64s(f, &env->tsc);
+ kvm_load_registers(env);
+ }
+#endif
return 0;
}
@@ -6727,6 +6956,15 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+#elif defined(TARGET_IA64)
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
#else
//#warning No CPU save/restore functions
@@ -6763,6 +7001,10 @@ static int ram_load_v1(QEMUFile *f, void *opaque)
if (qemu_get_be32(f) != phys_ram_size)
return -EINVAL;
for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
if (ret)
return ret;
@@ -6892,7 +7134,24 @@ static void ram_decompress_close(RamDecompressState *s)
inflateEnd(&s->zstream);
}
-static void ram_save(QEMUFile *f, void *opaque)
+static void ram_save_live(QEMUFile *f, void *opaque)
+{
+ target_ulong addr;
+
+ for (addr = 0; addr < phys_ram_size; addr += TARGET_PAGE_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (addr>=0xa0000) && (addr<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
+ if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) {
+ qemu_put_be32(f, addr);
+ qemu_put_buffer(f, phys_ram_base + addr, TARGET_PAGE_SIZE);
+ }
+ }
+ qemu_put_be32(f, 1);
+}
+
+static void ram_save_static(QEMUFile *f, void *opaque)
{
int i;
RamCompressState s1, *s = &s1;
@@ -6902,6 +7161,10 @@ static void ram_save(QEMUFile *f, void *opaque)
if (ram_compress_open(s, f) < 0)
return;
for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
#if 0
if (tight_savevm_enabled) {
int64_t sector_num;
@@ -6935,21 +7198,48 @@ static void ram_save(QEMUFile *f, void *opaque)
ram_compress_close(s);
}
-static int ram_load(QEMUFile *f, void *opaque, int version_id)
+static void ram_save(QEMUFile *f, void *opaque)
+{
+ int in_migration = cpu_physical_memory_get_dirty_tracking();
+
+ qemu_put_byte(f, in_migration);
+
+ if (in_migration)
+ ram_save_live(f, opaque);
+ else
+ ram_save_static(f, opaque);
+}
+
+static int ram_load_live(QEMUFile *f, void *opaque)
+{
+ target_ulong addr;
+
+ do {
+ addr = qemu_get_be32(f);
+ if (addr == 1)
+ break;
+
+ qemu_get_buffer(f, phys_ram_base + addr, TARGET_PAGE_SIZE);
+ } while (1);
+
+ return 0;
+}
+
+static int ram_load_static(QEMUFile *f, void *opaque)
{
RamDecompressState s1, *s = &s1;
uint8_t buf[10];
int i;
- if (version_id == 1)
- return ram_load_v1(f, opaque);
- if (version_id != 2)
- return -EINVAL;
if (qemu_get_be32(f) != phys_ram_size)
return -EINVAL;
if (ram_decompress_open(s, f) < 0)
return -EINVAL;
for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+#ifdef USE_KVM
+ if (kvm_allowed && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */
+ continue;
+#endif
if (ram_decompress_buf(s, buf, 1) < 0) {
fprintf(stderr, "Error while reading ram block header\n");
goto error;
@@ -6991,6 +7281,30 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+static int ram_load(QEMUFile *f, void *opaque, int version_id)
+{
+ int ret;
+
+ switch (version_id) {
+ case 1:
+ ret = ram_load_v1(f, opaque);
+ break;
+ case 3:
+ if (qemu_get_byte(f)) {
+ ret = ram_load_live(f, opaque);
+ break;
+ }
+ case 2:
+ ret = ram_load_static(f, opaque);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
/***********************************************************/
/* bottom halves (can be seen as timers which expire ASAP) */
@@ -7194,6 +7508,27 @@ static int reset_requested;
static int shutdown_requested;
static int powerdown_requested;
+int qemu_shutdown_requested(void)
+{
+ int r = shutdown_requested;
+ shutdown_requested = 0;
+ return r;
+}
+
+int qemu_reset_requested(void)
+{
+ int r = reset_requested;
+ reset_requested = 0;
+ return r;
+}
+
+int qemu_powerdown_requested(void)
+{
+ int r = powerdown_requested;
+ powerdown_requested = 0;
+ return r;
+}
+
void qemu_register_reset(QEMUResetHandler *func, void *opaque)
{
QEMUResetEntry **pre, *re;
@@ -7208,7 +7543,7 @@ void qemu_register_reset(QEMUResetHandler *func, void *opaque)
*pre = re;
}
-static void qemu_system_reset(void)
+void qemu_system_reset(void)
{
QEMUResetEntry *re;
@@ -7372,8 +7707,6 @@ void main_loop_wait(int timeout)
qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME],
qemu_get_clock(rt_clock));
- qemu_rearm_alarm_timer(alarm_timer);
-
/* Check bottom-halves last in case any of the earlier events triggered
them. */
qemu_bh_poll();
@@ -7388,6 +7721,14 @@ static int main_loop(void)
#endif
CPUState *env;
+
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ kvm_main_loop();
+ cpu_disable_ticks();
+ return 0;
+ }
+#endif
cur_cpu = first_cpu;
next_cpu = cur_cpu->next_cpu ?: first_cpu;
for(;;) {
@@ -7429,6 +7770,10 @@ static int main_loop(void)
if (reset_requested) {
reset_requested = 0;
qemu_system_reset();
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_load_registers(env);
+#endif
ret = EXCP_INTERRUPT;
}
if (powerdown_requested) {
@@ -7462,7 +7807,8 @@ static int main_loop(void)
static void help(int exitcode)
{
- printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2007 Fabrice Bellard\n"
+ printf("QEMU PC emulator version " QEMU_VERSION " (" KVM_VERSION ")"
+ ", Copyright (c) 2003-2007 Fabrice Bellard\n"
"usage: %s [options] [disk_image]\n"
"\n"
"'disk_image' is a raw hard image image for IDE hard disk 0\n"
@@ -7559,6 +7905,8 @@ static void help(int exitcode)
"\n"
"Debug/Expert options:\n"
"-monitor dev redirect the monitor to char device 'dev'\n"
+ "-vmchannel di:DI,dev redirect the hypercall device with device id DI, to char device 'dev'\n"
+ "-balloon dev redirect the balloon hypercall device to char device 'dev'\n"
"-serial dev redirect the serial port to char device 'dev'\n"
"-parallel dev redirect the parallel port to char device 'dev'\n"
"-pidfile file Write PID to 'file'\n"
@@ -7573,6 +7921,10 @@ static void help(int exitcode)
"-kernel-kqemu enable KQEMU full virtualization (default is user mode only)\n"
"-no-kqemu disable KQEMU kernel module usage\n"
#endif
+#ifdef USE_KVM
+ "-no-kvm disable KVM hardware virtualization\n"
+ "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n"
+#endif
#ifdef TARGET_I386
"-std-vga simulate a standard VGA card with VESA Bochs Extensions\n"
" (default is CL-GD5446 PCI VGA)\n"
@@ -7584,6 +7936,8 @@ static void help(int exitcode)
#ifndef _WIN32
"-daemonize daemonize QEMU after initializing\n"
#endif
+ "-tdf inject timer interrupts that got lost\n"
+ "-kvm-shadow-memory megs set the amount of shadow pages to be allocated\n"
"-option-rom rom load a file, rom, into the option ROM space\n"
#ifdef TARGET_SPARC
"-prom-env variable=value set OpenBIOS nvram variables\n"
@@ -7666,6 +8020,8 @@ enum {
QEMU_OPTION_std_vga,
QEMU_OPTION_echr,
QEMU_OPTION_monitor,
+ QEMU_OPTION_balloon,
+ QEMU_OPTION_vmchannel,
QEMU_OPTION_serial,
QEMU_OPTION_parallel,
QEMU_OPTION_loadvm,
@@ -7682,16 +8038,22 @@ enum {
QEMU_OPTION_smp,
QEMU_OPTION_vnc,
QEMU_OPTION_no_acpi,
+ QEMU_OPTION_no_kvm,
+ QEMU_OPTION_no_kvm_irqchip,
QEMU_OPTION_no_reboot,
QEMU_OPTION_show_cursor,
QEMU_OPTION_daemonize,
QEMU_OPTION_option_rom,
QEMU_OPTION_semihosting,
+ QEMU_OPTION_cpu_vendor,
QEMU_OPTION_name,
QEMU_OPTION_prom_env,
QEMU_OPTION_old_param,
QEMU_OPTION_clock,
QEMU_OPTION_startdate,
+ QEMU_OPTION_incoming,
+ QEMU_OPTION_tdf,
+ QEMU_OPTION_kvm_shadow_memory,
};
typedef struct QEMUOption {
@@ -7757,16 +8119,24 @@ const QEMUOption qemu_options[] = {
{ "no-kqemu", 0, QEMU_OPTION_no_kqemu },
{ "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu },
#endif
+#ifdef USE_KVM
+ { "no-kvm", 0, QEMU_OPTION_no_kvm },
+ { "no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip },
+#endif
#if defined(TARGET_PPC) || defined(TARGET_SPARC)
{ "g", 1, QEMU_OPTION_g },
#endif
{ "localtime", 0, QEMU_OPTION_localtime },
{ "std-vga", 0, QEMU_OPTION_std_vga },
+ { "monitor", 1, QEMU_OPTION_monitor },
+ { "balloon", 1, QEMU_OPTION_balloon },
+ { "vmchannel", 1, QEMU_OPTION_vmchannel },
{ "echr", HAS_ARG, QEMU_OPTION_echr },
{ "monitor", HAS_ARG, QEMU_OPTION_monitor },
{ "serial", HAS_ARG, QEMU_OPTION_serial },
{ "parallel", HAS_ARG, QEMU_OPTION_parallel },
{ "loadvm", HAS_ARG, QEMU_OPTION_loadvm },
+ { "incoming", 1, QEMU_OPTION_incoming },
{ "full-screen", 0, QEMU_OPTION_full_screen },
#ifdef CONFIG_SDL
{ "no-frame", 0, QEMU_OPTION_no_frame },
@@ -7791,10 +8161,13 @@ const QEMUOption qemu_options[] = {
#if defined(TARGET_ARM) || defined(TARGET_M68K)
{ "semihosting", 0, QEMU_OPTION_semihosting },
#endif
+ { "tdf", 0, QEMU_OPTION_tdf }, /* enable time drift fix */
+ { "kvm-shadow-memory", HAS_ARG, QEMU_OPTION_kvm_shadow_memory },
{ "name", HAS_ARG, QEMU_OPTION_name },
#if defined(TARGET_SPARC)
{ "prom-env", HAS_ARG, QEMU_OPTION_prom_env },
#endif
+ { "cpu-vendor", HAS_ARG, QEMU_OPTION_cpu_vendor },
#if defined(TARGET_ARM)
{ "old-param", 0, QEMU_OPTION_old_param },
#endif
@@ -7894,6 +8267,8 @@ static void register_machines(void)
qemu_register_machine(&dummy_m68k_machine);
#elif defined(TARGET_CRIS)
qemu_register_machine(&bareetraxfs_machine);
+#elif defined(TARGET_IA64)
+ qemu_register_machine(&ipf_machine);
#else
#error unsupported CPU
#endif
@@ -8024,6 +8399,17 @@ static BOOL WINAPI qemu_ctrl_handler(DWORD type)
#define MAX_NET_CLIENTS 32
+static int saved_argc;
+static char **saved_argv;
+
+void qemu_get_launch_info(int *argc, char ***argv, int *opt_daemonize, const char **opt_incoming)
+{
+ *argc = saved_argc;
+ *argv = saved_argv;
+ *opt_daemonize = daemonize;
+ *opt_incoming = incoming;
+}
+
int main(int argc, char **argv)
{
#ifdef CONFIG_GDBSTUB
@@ -8045,6 +8431,8 @@ int main(int argc, char **argv)
const char *r, *optarg;
CharDriverState *monitor_hd;
char monitor_device[128];
+ char vmchannel_devices[MAX_VMCHANNEL_DEVICES][128];
+ int vmchannel_device_index;
char serial_devices[MAX_SERIAL_PORTS][128];
int serial_device_index;
char parallel_devices[MAX_PARALLEL_PORTS][128];
@@ -8058,6 +8446,9 @@ int main(int argc, char **argv)
const char *pid_file = NULL;
VLANState *vlan;
+ saved_argc = argc;
+ saved_argv = argv;
+
LIST_INIT (&vm_change_state_head);
#ifndef _WIN32
{
@@ -8107,6 +8498,10 @@ int main(int argc, char **argv)
translation = BIOS_ATA_TRANSLATION_AUTO;
pstrcpy(monitor_device, sizeof(monitor_device), "vc");
+ for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++)
+ vmchannel_devices[i][0] = '\0';
+ vmchannel_device_index = 0;
+
pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "vc");
for(i = 1; i < MAX_SERIAL_PORTS; i++)
serial_devices[i][0] = '\0';
@@ -8372,7 +8767,7 @@ int main(int argc, char **argv)
help(0);
break;
case QEMU_OPTION_m:
- ram_size = atoi(optarg) * 1024 * 1024;
+ ram_size = (int64_t)atoi(optarg) * 1024 * 1024;
if (ram_size <= 0)
help(1);
if (ram_size > PHYS_RAM_MAX_SIZE) {
@@ -8477,6 +8872,28 @@ int main(int argc, char **argv)
case QEMU_OPTION_monitor:
pstrcpy(monitor_device, sizeof(monitor_device), optarg);
break;
+ case QEMU_OPTION_balloon:
+ if (vmchannel_device_index >= MAX_VMCHANNEL_DEVICES) {
+ fprintf(stderr, "qemu: too many balloon/vmchannel devices\n");
+ exit(1);
+ }
+ if (balloon_used) {
+ fprintf(stderr, "qemu: only one balloon device can be used\n");
+ exit(1);
+ }
+ sprintf(vmchannel_devices[vmchannel_device_index],"di:cdcd,%s", optarg);
+ vmchannel_device_index++;
+ balloon_used = 1;
+ break;
+ case QEMU_OPTION_vmchannel:
+ if (vmchannel_device_index >= MAX_VMCHANNEL_DEVICES) {
+ fprintf(stderr, "qemu: too many balloon/vmchannel devices\n");
+ exit(1);
+ }
+ pstrcpy(vmchannel_devices[vmchannel_device_index],
+ sizeof(vmchannel_devices[0]), optarg);
+ vmchannel_device_index++;
+ break;
case QEMU_OPTION_serial:
if (serial_device_index >= MAX_SERIAL_PORTS) {
fprintf(stderr, "qemu: too many serial ports\n");
@@ -8498,6 +8915,9 @@ int main(int argc, char **argv)
case QEMU_OPTION_loadvm:
loadvm = optarg;
break;
+ case QEMU_OPTION_incoming:
+ incoming = optarg;
+ break;
case QEMU_OPTION_full_screen:
full_screen = 1;
break;
@@ -8528,6 +8948,14 @@ int main(int argc, char **argv)
kqemu_allowed = 2;
break;
#endif
+#ifdef USE_KVM
+ case QEMU_OPTION_no_kvm:
+ kvm_allowed = 0;
+ break;
+ case QEMU_OPTION_no_kvm_irqchip:
+ kvm_irqchip = 0;
+ break;
+#endif
case QEMU_OPTION_usb:
usb_enabled = 1;
break;
@@ -8575,6 +9003,12 @@ int main(int argc, char **argv)
case QEMU_OPTION_semihosting:
semihosting_enabled = 1;
break;
+ 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_name:
qemu_name = optarg;
break;
@@ -8588,6 +9022,9 @@ int main(int argc, char **argv)
nb_prom_envs++;
break;
#endif
+ case QEMU_OPTION_cpu_vendor:
+ cpu_vendor_string = optarg;
+ break;
#ifdef TARGET_ARM
case QEMU_OPTION_old_param:
old_param = 1;
@@ -8636,11 +9073,6 @@ int main(int argc, char **argv)
}
#ifndef _WIN32
- if (daemonize && !nographic && vnc_display == NULL) {
- fprintf(stderr, "Can only daemonize if using -nographic or -vnc\n");
- daemonize = 0;
- }
-
if (daemonize) {
pid_t pid;
@@ -8678,7 +9110,6 @@ int main(int argc, char **argv)
exit(1);
umask(027);
- chdir("/");
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
@@ -8686,6 +9117,15 @@ int main(int argc, char **argv)
}
#endif
+#if USE_KVM
+ if (kvm_allowed) {
+ if (kvm_qemu_init() < 0) {
+ fprintf(stderr, "Could not initialize KVM, will disable KVM support\n");
+ kvm_allowed = 0;
+ }
+ }
+#endif
+
if (pid_file && qemu_create_pidfile(pid_file) != 0) {
if (daemonize) {
uint8_t status = 1;
@@ -8780,11 +9220,47 @@ int main(int argc, char **argv)
/* init the memory */
phys_ram_size = ram_size + vga_ram_size + MAX_BIOS_SIZE;
+#if USE_KVM
+ /* Initialize kvm */
+#ifdef TARGET_IA64
+#define KVM_EXTRA_PAGES 0
+#else
+#define KVM_EXTRA_PAGES 3
+#endif
+ if (kvm_allowed) {
+ phys_ram_size += KVM_EXTRA_PAGES * TARGET_PAGE_SIZE;
+ if (kvm_qemu_create_context() < 0) {
+ fprintf(stderr, "Could not create KVM context\n");
+ exit(1);
+ }
+#ifdef KVM_CAP_USER_MEMORY
+{
+ int ret;
+
+ ret = kvm_qemu_check_extension(KVM_CAP_USER_MEMORY);
+ if (ret) {
+ phys_ram_base = qemu_vmalloc(phys_ram_size);
+ if (!phys_ram_base) {
+ fprintf(stderr, "Could not allocate physical memory\n");
+ exit(1);
+ }
+ }
+}
+#endif
+ } else {
+ phys_ram_base = qemu_vmalloc(phys_ram_size);
+ if (!phys_ram_base) {
+ fprintf(stderr, "Could not allocate physical memory\n");
+ exit(1);
+ }
+ }
+#else
phys_ram_base = qemu_vmalloc(phys_ram_size);
if (!phys_ram_base) {
fprintf(stderr, "Could not allocate physical memory\n");
exit(1);
}
+#endif
bdrv_init();
@@ -8810,7 +9286,7 @@ int main(int argc, char **argv)
exit(1);
register_savevm("timer", 0, 2, timer_save, timer_load, NULL);
- register_savevm("ram", 0, 2, ram_save, ram_load, NULL);
+ register_savevm("ram", 0, 3, ram_save, ram_load, NULL);
init_ioports();
@@ -8855,6 +9331,31 @@ int main(int argc, char **argv)
monitor_init(monitor_hd, !nographic);
}
+ for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++) {
+ const char *devname = vmchannel_devices[i];
+ if (devname[0] != '\0' && strcmp(devname, "none")) {
+ int devid;
+ char *termn;
+
+ if (strstart(devname, "di:", &devname)) {
+ devid = strtol(devname, &termn, 16);
+ devname = termn + 1;
+ }
+ else {
+ fprintf(stderr, "qemu: could not find vmchannel device id '%s'\n",
+ devname);
+ exit(1);
+ }
+ vmchannel_hds[i] = qemu_chr_open(devname);
+ if (!vmchannel_hds[i]) {
+ fprintf(stderr, "qemu: could not open vmchannel device '%s'\n",
+ devname);
+ exit(1);
+ }
+ vmchannel_init(vmchannel_hds[i], devid, i);
+ }
+ }
+
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
const char *devname = serial_devices[i];
if (devname[0] != '\0' && strcmp(devname, "none")) {
@@ -8901,6 +9402,11 @@ int main(int argc, char **argv)
qemu_mod_timer(display_state.gui_timer, qemu_get_clock(rt_clock));
}
+#ifdef USE_KVM
+ if (kvm_allowed)
+ kvm_init_ap();
+#endif
+
#ifdef CONFIG_GDBSTUB
if (use_gdbstub) {
/* XXX: use standard host:port notation and modify options
@@ -8912,10 +9418,19 @@ int main(int argc, char **argv)
}
}
#endif
-
if (loadvm)
do_loadvm(loadvm);
+ if (incoming) {
+ int rc;
+
+ rc = migrate_incoming(incoming);
+ if (rc != 0) {
+ fprintf(stderr, "Migration failed rc=%d\n", rc);
+ exit(rc);
+ }
+ }
+
{
/* XXX: simplify init */
read_passwords();
@@ -8937,6 +9452,7 @@ int main(int argc, char **argv)
if (len != 1)
exit(1);
+ chdir("/");
TFR(fd = open("/dev/null", O_RDWR));
if (fd == -1)
exit(1);
diff --git a/vnc.c b/vnc.c
index 9fb8c85a1..f1eeedb9a 100644
--- a/vnc.c
+++ b/vnc.c
@@ -1260,6 +1260,7 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
char pad[3] = { 0, 0, 0 };
char buf[1024];
int size;
+ unsigned char *prog = "QEMU";
vs->width = vs->ds->width;
vs->height = vs->ds->height;
@@ -1304,10 +1305,15 @@ static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
vnc_write(vs, pad, 3); /* padding */
+#if USE_KVM
+ if (kvm_allowed)
+ prog = "QEMU/KVM";
+#endif
+
if (qemu_name)
- size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
+ size = snprintf(buf, sizeof(buf), "%s (%s)", prog, qemu_name);
else
- size = snprintf(buf, sizeof(buf), "QEMU");
+ size = snprintf(buf, sizeof(buf), "%s", prog);
vnc_write_u32(vs, size);
vnc_write(vs, buf, size);