aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--Makefile.target99
-rw-r--r--block-raw-posix.c70
-rw-r--r--block-vmdk.c1
-rw-r--r--block.c23
-rw-r--r--block_int.h2
-rw-r--r--compatfd.c131
-rw-r--r--compatfd.h28
-rwxr-xr-xconfigure128
-rw-r--r--cpu-all.h2
-rw-r--r--cpu-defs.h19
-rw-r--r--cpu-exec.c14
-rw-r--r--cutils.c39
-rw-r--r--exec.c86
-rw-r--r--gdbstub.c26
-rw-r--r--gdbstub.h7
-rw-r--r--hw/acpi.c259
-rw-r--r--hw/apic.c190
-rw-r--r--hw/boards.h5
-rw-r--r--hw/cirrus_vga.c43
-rw-r--r--hw/device-assignment.c680
-rw-r--r--hw/device-assignment.h107
-rw-r--r--hw/device-hotplug.c264
-rw-r--r--hw/ds1225y.c4
-rw-r--r--hw/e1000.c19
-rw-r--r--hw/eepro100.c17
-rw-r--r--hw/extboot.c132
-rw-r--r--hw/i8254-kvm.c108
-rw-r--r--hw/i8254.c63
-rw-r--r--hw/i8254.h66
-rw-r--r--hw/i8259.c97
-rw-r--r--hw/ide.c6
-rw-r--r--hw/ipf.c708
-rw-r--r--hw/irq.c6
-rw-r--r--hw/irq.h2
-rw-r--r--hw/lsi53c895a.c15
-rw-r--r--hw/mips_malta.c6
-rw-r--r--hw/ne2000.c7
-rw-r--r--hw/pc.c124
-rw-r--r--hw/pc.h20
-rw-r--r--hw/pci.c130
-rw-r--r--hw/pci.h28
-rw-r--r--hw/pcnet.c5
-rw-r--r--hw/piix_pci.c16
-rw-r--r--hw/ppc4xx.h3
-rw-r--r--hw/ppc_chrp.c3
-rw-r--r--hw/ppc_prep.c3
-rw-r--r--hw/realview.c3
-rw-r--r--hw/rtl8139.c6
-rw-r--r--hw/sun4u.c3
-rw-r--r--hw/versatilepb.c3
-rw-r--r--hw/vga.c199
-rw-r--r--hw/vga_int.h4
-rw-r--r--hw/virtio-balloon.c1
-rw-r--r--hw/virtio-blk.c1
-rw-r--r--hw/virtio-net.c85
-rw-r--r--hw/virtio.c4
-rw-r--r--hw/vmport.c13
-rw-r--r--ia64.ld2
-rw-r--r--ia64intrin.h150
-rw-r--r--kvm-tpr-opt.c378
-rw-r--r--kvm.h4
-rw-r--r--kvm/.gitignore62
-rw-r--r--kvm/Makefile124
-rw-r--r--kvm/bios/.cvsignore1
-rw-r--r--kvm/bios/Makefile130
-rw-r--r--kvm/bios/Makefile.in118
-rwxr-xr-xkvm/bios/acpi-dsdt.dsl785
-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.c11450
-rw-r--r--kvm/bios/rombios.h70
-rwxr-xr-xkvm/bios/rombios32.c2260
-rw-r--r--kvm/bios/rombios32.ld22
-rw-r--r--kvm/bios/rombios32start.S123
-rw-r--r--kvm/bios/usage.cc99
-rw-r--r--kvm/bios/vapic.S294
-rwxr-xr-xkvm/configure172
-rw-r--r--kvm/doxygen.conf1252
-rw-r--r--kvm/extboot/Makefile41
-rw-r--r--kvm/extboot/STATUS6
-rw-r--r--kvm/extboot/extboot.S692
-rw-r--r--kvm/extboot/signrom.c79
-rw-r--r--kvm/kernel/Kbuild2
-rw-r--r--kvm/kernel/Makefile129
-rw-r--r--kvm/kernel/anon_inodes.c268
-rw-r--r--kvm/kernel/external-module-compat-comm.h694
-rw-r--r--kvm/kernel/external-module-compat.c300
-rw-r--r--kvm/kernel/ia64/Kbuild12
-rw-r--r--kvm/kernel/ia64/Makefile.pre27
-rw-r--r--kvm/kernel/ia64/external-module-compat.h39
-rw-r--r--kvm/kernel/ia64/hack-module.awk25
-rw-r--r--kvm/kernel/include-compat/asm-x86/asm.h3
-rw-r--r--kvm/kernel/include-compat/asm-x86/cmpxchg.h3
-rw-r--r--kvm/kernel/include-compat/asm-x86/msidef.h55
-rw-r--r--kvm/kernel/include-compat/asm-x86/msr-index.h339
-rw-r--r--kvm/kernel/include-compat/asm-x86/pvclock-abi.h42
-rw-r--r--kvm/kernel/include-compat/linux/anon_inodes.h16
-rw-r--r--kvm/kernel/include-compat/linux/intel-iommu.h355
-rw-r--r--kvm/kernel/include-compat/linux/iova.h52
-rw-r--r--kvm/kernel/include-compat/linux/magic.h41
-rw-r--r--kvm/kernel/include-compat/linux/marker.h119
-rw-r--r--kvm/kernel/include-compat/linux/math64.h3
-rw-r--r--kvm/kernel/include-compat/linux/mmu_notifier.h6
-rw-r--r--kvm/kernel/include-compat/linux/msi.h50
-rw-r--r--kvm/kernel/include-compat/linux/mutex.h3
-rw-r--r--kvm/kernel/kvm-kmod.spec52
-rw-r--r--kvm/kernel/powerpc/Makefile.pre1
-rw-r--r--kvm/kernel/powerpc/hack-module.awk5
-rw-r--r--kvm/kernel/unifdef.h40
-rw-r--r--kvm/kernel/x86/Kbuild18
-rw-r--r--kvm/kernel/x86/Makefile.pre1
-rw-r--r--kvm/kernel/x86/debug.h23
-rw-r--r--kvm/kernel/x86/external-module-compat.h390
-rw-r--r--kvm/kernel/x86/hack-module.awk96
-rw-r--r--kvm/kernel/x86/preempt.c253
-rw-r--r--kvm/kernel/x86/vmx-debug.c1078
-rwxr-xr-xkvm/kvm283
-rw-r--r--kvm/kvm.spec139
-rwxr-xr-xkvm/kvm_stat129
-rw-r--r--kvm/libfdt/Makefile19
-rw-r--r--kvm/libfdt/README3
-rw-r--r--kvm/libfdt/fdt.c194
-rw-r--r--kvm/libfdt/fdt.h60
-rw-r--r--kvm/libfdt/fdt_ro.c476
-rw-r--r--kvm/libfdt/fdt_rw.c467
-rw-r--r--kvm/libfdt/fdt_strerror.c96
-rw-r--r--kvm/libfdt/fdt_sw.c258
-rw-r--r--kvm/libfdt/fdt_wip.c144
-rw-r--r--kvm/libfdt/libfdt.h1076
-rw-r--r--kvm/libfdt/libfdt_env.h22
-rw-r--r--kvm/libfdt/libfdt_internal.h96
-rw-r--r--kvm/libkvm/Makefile37
-rw-r--r--kvm/libkvm/config-i386.mak6
-rw-r--r--kvm/libkvm/config-ia64.mak5
-rw-r--r--kvm/libkvm/config-powerpc.mak4
-rw-r--r--kvm/libkvm/config-s390.mak3
-rw-r--r--kvm/libkvm/config-s390x.mak3
-rw-r--r--kvm/libkvm/config-x86_64.mak6
-rw-r--r--kvm/libkvm/kvm-common.h94
-rw-r--r--kvm/libkvm/kvm-ia64.h31
-rw-r--r--kvm/libkvm/kvm-powerpc.h36
-rw-r--r--kvm/libkvm/kvm-s390.h31
-rw-r--r--kvm/libkvm/kvm-x86.h55
-rw-r--r--kvm/libkvm/libkvm-ia64.c82
-rw-r--r--kvm/libkvm/libkvm-powerpc.c100
-rw-r--r--kvm/libkvm/libkvm-s390.c110
-rw-r--r--kvm/libkvm/libkvm-x86.c543
-rw-r--r--kvm/libkvm/libkvm.c1146
-rw-r--r--kvm/libkvm/libkvm.h721
-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/Makefile59
-rwxr-xr-xkvm/user/balloon_ctl.c92
-rw-r--r--kvm/user/bootstrap.lds15
-rw-r--r--kvm/user/config-i386.mak10
-rw-r--r--kvm/user/config-ia64.mak7
-rw-r--r--kvm/user/config-powerpc-440.mak15
-rw-r--r--kvm/user/config-powerpc.mak39
-rw-r--r--kvm/user/config-x86-common.mak64
-rw-r--r--kvm/user/config-x86_64.mak13
-rwxr-xr-xkvm/user/configure75
-rw-r--r--kvm/user/flat.lds17
-rw-r--r--kvm/user/formats31
-rw-r--r--kvm/user/iotable.c53
-rw-r--r--kvm/user/iotable.h40
-rw-r--r--kvm/user/kvmtrace.c706
-rwxr-xr-xkvm/user/kvmtrace_format527
-rw-r--r--kvm/user/main-ppc.c383
-rw-r--r--kvm/user/main.c611
-rw-r--r--kvm/user/test/lib/libcflat.h36
-rw-r--r--kvm/user/test/lib/panic.c13
-rw-r--r--kvm/user/test/lib/powerpc/44x/map.c51
-rw-r--r--kvm/user/test/lib/powerpc/44x/timebase.S28
-rw-r--r--kvm/user/test/lib/powerpc/44x/timebase.h25
-rw-r--r--kvm/user/test/lib/powerpc/44x/tlbwe.S29
-rw-r--r--kvm/user/test/lib/powerpc/io.c35
-rw-r--r--kvm/user/test/lib/printf.c179
-rw-r--r--kvm/user/test/lib/string.c21
-rw-r--r--kvm/user/test/lib/x86/fake-apic.h14
-rw-r--r--kvm/user/test/lib/x86/io.c23
-rw-r--r--kvm/user/test/lib/x86/smp.c150
-rw-r--r--kvm/user/test/lib/x86/smp.h16
-rw-r--r--kvm/user/test/powerpc/44x/tlbsx.S33
-rw-r--r--kvm/user/test/powerpc/44x/tlbwe.S27
-rw-r--r--kvm/user/test/powerpc/44x/tlbwe_16KB.S35
-rw-r--r--kvm/user/test/powerpc/44x/tlbwe_hole.S27
-rw-r--r--kvm/user/test/powerpc/cstart.S38
-rw-r--r--kvm/user/test/powerpc/exit.c23
-rw-r--r--kvm/user/test/powerpc/helloworld.c27
-rw-r--r--kvm/user/test/powerpc/io.S32
-rw-r--r--kvm/user/test/powerpc/spin.S4
-rw-r--r--kvm/user/test/powerpc/sprg.S7
-rw-r--r--kvm/user/test/x86/access.c580
-rw-r--r--kvm/user/test/x86/apic.c351
-rw-r--r--kvm/user/test/x86/apic.h133
-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.c259
-rw-r--r--kvm/user/test/x86/exit.c7
-rw-r--r--kvm/user/test/x86/hypercall.c31
-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/memtest1.S44
-rw-r--r--kvm/user/test/x86/port80.c12
-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/realmode.c415
-rw-r--r--kvm/user/test/x86/realmode.lds16
-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.c31
-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/tsc.c40
-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--kvm/vgabios/.cvsignore1
-rw-r--r--kvm/vgabios/BUGS3
-rw-r--r--kvm/vgabios/COPYING504
-rw-r--r--kvm/vgabios/ChangeLog1264
-rw-r--r--kvm/vgabios/Makefile87
-rw-r--r--kvm/vgabios/Notes11
-rw-r--r--kvm/vgabios/README219
-rw-r--r--kvm/vgabios/TODO26
-rw-r--r--kvm/vgabios/biossums.c282
-rw-r--r--kvm/vgabios/clext.c1688
-rwxr-xr-xkvm/vgabios/dataseghack23
-rw-r--r--kvm/vgabios/tests/lfbprof/Makefile5
-rw-r--r--kvm/vgabios/tests/lfbprof/lfbprof.c594
-rw-r--r--kvm/vgabios/tests/lfbprof/lfbprof.h149
-rw-r--r--kvm/vgabios/tests/testbios.c353
-rw-r--r--kvm/vgabios/vbe.c1432
-rw-r--r--kvm/vgabios/vbe.h313
-rw-r--r--kvm/vgabios/vbe_display_api.txt237
-rw-r--r--kvm/vgabios/vbetables-gen.c264
-rw-r--r--kvm/vgabios/vgabios.c3853
-rw-r--r--kvm/vgabios/vgabios.h47
-rw-r--r--kvm/vgabios/vgafonts.h784
-rw-r--r--kvm/vgabios/vgatables.h622
-rw-r--r--linux-user/main.c1
-rw-r--r--monitor.c41
-rw-r--r--net.c244
-rw-r--r--net.h11
-rw-r--r--osdep.c4
-rw-r--r--pc-bios/Makefile5
-rw-r--r--pc-bios/bios-vista.diff17
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/bochs-manifest24
-rw-r--r--pc-bios/openbios-sparcbin0 -> 506966 bytes
-rw-r--r--pc-bios/vgabios-cirrus.binbin35840 -> 35840 bytes
-rw-r--r--pc-bios/vgabios.binbin38400 -> 39936 bytes
-rw-r--r--qemu-common.h11
-rw-r--r--qemu-doc.texi10
-rw-r--r--qemu-kvm-helper.c40
-rw-r--r--qemu-kvm-ia64.c146
-rw-r--r--qemu-kvm-x86.c900
-rw-r--r--qemu-kvm.c1359
-rw-r--r--qemu-kvm.h204
-rw-r--r--qemu-lock.h4
-rw-r--r--qemu-tool.c4
-rw-r--r--savevm.c1
-rw-r--r--sysemu.h35
-rw-r--r--target-i386/cpu.h1
-rw-r--r--target-i386/fake-exec.c54
-rw-r--r--target-i386/helper.c12
-rw-r--r--target-i386/kvm.c5
-rw-r--r--target-i386/machine.c29
-rw-r--r--target-ia64/cpu.h84
-rw-r--r--target-ia64/exec.h57
-rw-r--r--target-ia64/fake-exec.c59
-rw-r--r--target-ia64/firmware.c592
-rw-r--r--target-ia64/firmware.h44
-rw-r--r--target-ia64/helper.c5
-rw-r--r--target-ia64/machine.c31
-rw-r--r--target-ia64/op.c22
-rw-r--r--target-ia64/op_helper.c98
-rw-r--r--target-ia64/translate.c39
-rw-r--r--target-ppc/cpu.h10
-rw-r--r--target-ppc/fake-exec.c104
-rw-r--r--usb-linux.c4
-rw-r--r--vl.c431
292 files changed, 58403 insertions, 283 deletions
diff --git a/Makefile b/Makefile
index 85523d775..8422281ef 100644
--- a/Makefile
+++ b/Makefile
@@ -62,6 +62,10 @@ endif
BLOCK_OBJS += block-raw-posix.o
endif
+ifdef CONFIG_AIO
+BLOCK_OBJS += compatfd.o
+endif
+
######################################################################
# libqemu_common.a: Target independent part of system emulation. The
# long term path is to suppress *all* target specific code in case of
@@ -223,6 +227,7 @@ ifdef INSTALL_BLOBS
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
video.x openbios-sparc32 openbios-sparc64 pxe-ne2k_pci.bin \
pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin bamboo.dtb
+BLOBS += extboot.bin
else
BLOBS=
endif
@@ -245,6 +250,7 @@ endif
ifneq ($(BLOBS),)
mkdir -p "$(DESTDIR)$(datadir)"
set -e; for x in $(BLOBS); do \
+ [ -f $(SRC_PATH)/pc-bios/$$x ] && \
$(INSTALL) -m 644 $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
done
endif
@@ -358,6 +364,7 @@ tarbin:
$(datadir)/pxe-rtl8139.bin \
$(datadir)/pxe-pcnet.bin \
$(datadir)/pxe-e1000.bin \
+ $(datadir)/extboot.bin \
$(docdir)/qemu-doc.html \
$(docdir)/qemu-tech.html \
$(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1
diff --git a/Makefile.target b/Makefile.target
index b66b69924..f58015b1a 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 -MT $@ -MP -DNEED_CPU_H
@@ -161,7 +168,7 @@ CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS)
LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS)
OP_CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS)
-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
ifdef CONFIG_WIN32
LIBS+=-lwinmm -lws2_32 -liphlpapi
@@ -183,14 +190,23 @@ all: $(PROGS)
#########################################################
# cpu emulator library
-LIBOBJS=exec.o kqemu.o translate-all.o cpu-exec.o\
- translate.o host-utils.o
+LIBOBJS=exec.o kqemu.o cpu-exec.o host-utils.o
+
+ifeq ($(NO_CPU_EMULATION), 1)
+LIBOBJS+=fake-exec.o
+else
+LIBOBJS+= translate-all.o translate.o
# TCG code generator
LIBOBJS+= tcg/tcg.o tcg/tcg-runtime.o
CPPFLAGS+=-I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/$(ARCH)
+endif
ifeq ($(ARCH),sparc64)
CPPFLAGS+=-I$(SRC_PATH)/tcg/sparc
endif
+
+ifeq ($(USE_KVM), 1)
+LIBOBJS+=qemu-kvm.o
+endif
ifdef CONFIG_SOFTFLOAT
LIBOBJS+=fpu/softfloat.o
else
@@ -199,6 +215,28 @@ endif
CPPFLAGS+=-I$(SRC_PATH)/fpu
LIBOBJS+= op_helper.o helper.o
+ifeq ($(TARGET_ARCH), i386)
+LIBOBJS+=helper.o
+ifeq ($(USE_KVM), 1)
+LIBOBJS+=qemu-kvm-x86.o kvm-tpr-opt.o
+LIBOBJS+=qemu-kvm-helper.o
+endif
+endif
+
+ifeq ($(TARGET_ARCH), x86_64)
+LIBOBJS+=helper.o
+ifeq ($(USE_KVM), 1)
+LIBOBJS+=qemu-kvm-x86.o kvm-tpr-opt.o
+LIBOBJS+=qemu-kvm-helper.o
+endif
+endif
+
+LIBOBJS+= op_helper.o
+
+ifneq ($(TARGET_ARCH), ia64)
+LIBOBJS+= helper.o
+endif
+
ifeq ($(TARGET_BASE_ARCH), arm)
LIBOBJS+= neon_helper.o iwmmxt_helper.o
endif
@@ -207,6 +245,13 @@ ifeq ($(TARGET_BASE_ARCH), alpha)
LIBOBJS+= alpha_palcode.o
endif
+ifeq ($(TARGET_BASE_ARCH), ia64)
+LIBOBJS+=op_helper.o firmware.o
+ifeq ($(USE_KVM), 1)
+LIBOBJS+=qemu-kvm-ia64.o
+endif
+endif
+
ifeq ($(TARGET_BASE_ARCH), cris)
LIBOBJS+= cris-dis.o
@@ -215,6 +260,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)
@@ -267,7 +313,7 @@ translate-all.o: translate-all.c cpu.h
tcg/tcg.o: cpu.h
machine.o: machine.c
- $(CC) $(OP_CFLAGS) $(CPPFLAGS) -c -o $@ $<
+ $(CC) $(OP_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
# HELPER_CFLAGS is used for all the code compiled with static register
# variables
@@ -277,6 +323,9 @@ op_helper.o: op_helper.c
cpu-exec.o: cpu-exec.c
$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+qemu-kvm-helper.o: qemu-kvm-helper.c
+ $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
#########################################################
# Linux user emulator target
@@ -573,6 +622,10 @@ endif
OBJS+=block-raw-posix.o
endif
+ifdef CONFIG_AIO
+OBJS+=compatfd.o
+endif
+
LIBS+=-lz
ifdef CONFIG_ALSA
LIBS += -lasound
@@ -608,6 +661,12 @@ ifdef CONFIG_CS4231A
SOUND_HW += cs4231a.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)
@@ -633,18 +692,42 @@ OBJS += pcnet.o
OBJS += rtl8139.o
OBJS += e1000.o
+OBJS += device-hotplug.o
+
+ifeq ($(USE_KVM_DEVICE_ASSIGNMENT), 1)
+OBJS+= device-assignment.o
+endif
+
ifeq ($(TARGET_BASE_ARCH), i386)
# Hardware support
OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
+OBJS+= extboot.o
+# virtio support
+OBJS+= virtio.o virtio-blk.o virtio-balloon.o
+OBJS += virtio-net.o
+ifeq ($(USE_KVM_PIT), 1)
+OBJS+= i8254-kvm.o
+endif
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
endif
+ifeq ($(TARGET_BASE_ARCH), ia64)
+# Hardware support
+OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
+OBJS+= fdc.o mc146818rtc.o serial.o i8259.o ipf.o
+OBJS+= cirrus_vga.o parallel.o acpi.o piix_pci.o
+OBJS+= usb-uhci.o smbus_eeprom.o
+# virtio support
+OBJS+= virtio.o virtio-blk.o virtio-balloon.o
+OBJS+= virtio-net.o
+endif
ifeq ($(TARGET_BASE_ARCH), ppc)
CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
# shared objects
OBJS+= ppc.o ide.o vga.o $(SOUND_HW) dma.o openpic.o
+OBJS+= cirrus_vga.o
# PREP target
OBJS+= pckbd.o ps2.o serial.o i8259.o i8254.o fdc.o m48t59.o mc146818rtc.o
OBJS+= prep_pci.o ppc_prep.o
@@ -772,8 +855,12 @@ ifdef TARGET_GPROF
LDFLAGS+=-p
main.o: CFLAGS+=-p
endif
+ifeq ($(TARGET_ARCH), ia64)
+firmware.o: firmware.c
+ $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+endif
-$(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a
+$(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a $(DEPLIBS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(BRLAPI_LIBS) $(VDE_LIBS)
endif # !CONFIG_USER_ONLY
@@ -798,7 +885,7 @@ clean:
install: all
ifneq ($(PROGS),)
- $(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
+ $(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
endif
# Include automatically generated dependency files
diff --git a/block-raw-posix.c b/block-raw-posix.c
index 2fbb714d0..820de243a 100644
--- a/block-raw-posix.c
+++ b/block-raw-posix.c
@@ -25,6 +25,7 @@
#include "qemu-timer.h"
#include "qemu-char.h"
#include "block_int.h"
+#include "compatfd.h"
#include <assert.h>
#ifdef CONFIG_AIO
#include "posix-aio-compat.h"
@@ -444,7 +445,7 @@ typedef struct RawAIOCB {
typedef struct PosixAioState
{
- int rfd, wfd;
+ int fd;
RawAIOCB *first_aio;
} PosixAioState;
@@ -453,18 +454,29 @@ static void posix_aio_read(void *opaque)
PosixAioState *s = opaque;
RawAIOCB *acb, **pacb;
int ret;
- ssize_t len;
-
- /* read all bytes from signal pipe */
- for (;;) {
- char bytes[16];
-
- len = read(s->rfd, bytes, sizeof(bytes));
+ size_t offset;
+ union {
+ struct qemu_signalfd_siginfo siginfo;
+ char buf[128];
+ } sig;
+
+ /* try to read from signalfd, don't freak out if we can't read anything */
+ offset = 0;
+ while (offset < 128) {
+ ssize_t len;
+
+ len = read(s->fd, sig.buf + offset, 128 - offset);
if (len == -1 && errno == EINTR)
- continue; /* try again */
- if (len == sizeof(bytes))
- continue; /* more to read */
- break;
+ continue;
+ if (len == -1 && errno == EAGAIN) {
+ /* there is no natural reason for this to happen,
+ * so we'll spin hard until we get everything just
+ * to be on the safe side. */
+ if (offset > 0)
+ continue;
+ }
+
+ offset += len;
}
for(;;) {
@@ -511,20 +523,9 @@ static int posix_aio_flush(void *opaque)
static PosixAioState *posix_aio_state;
-static void aio_signal_handler(int signum)
-{
- if (posix_aio_state) {
- char byte = 0;
-
- write(posix_aio_state->wfd, &byte, sizeof(byte));
- }
-
- qemu_service_io();
-}
-
static int posix_aio_init(void)
{
- struct sigaction act;
+ sigset_t mask;
PosixAioState *s;
int fds[2];
struct qemu_paioinit ai;
@@ -536,24 +537,21 @@ static int posix_aio_init(void)
if (s == NULL)
return -ENOMEM;
- sigfillset(&act.sa_mask);
- act.sa_flags = 0; /* do not restart syscalls to interrupt select() */
- act.sa_handler = aio_signal_handler;
- sigaction(SIGUSR2, &act, NULL);
+ /* Make sure to block AIO signal */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
s->first_aio = NULL;
- if (pipe(fds) == -1) {
- fprintf(stderr, "failed to create pipe\n");
+ s->fd = qemu_signalfd(&mask);
+ if (s->fd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
return -errno;
}
- s->rfd = fds[0];
- s->wfd = fds[1];
-
- fcntl(s->rfd, F_SETFL, O_NONBLOCK);
- fcntl(s->wfd, F_SETFL, O_NONBLOCK);
+ fcntl(s->fd, F_SETFL, O_NONBLOCK);
- qemu_aio_set_fd_handler(s->rfd, posix_aio_read, NULL, posix_aio_flush, s);
+ qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush, s);
memset(&ai, 0, sizeof(ai));
ai.aio_threads = 64;
diff --git a/block-vmdk.c b/block-vmdk.c
index eefd76bea..cab20781d 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 28d63d7e6..dc744dd31 100644
--- a/block.c
+++ b/block.c
@@ -30,6 +30,7 @@
#include "qemu-common.h"
#include "console.h"
#include "block_int.h"
+#include "osdep.h"
#ifdef _BSD
#include <sys/types.h>
@@ -761,20 +762,26 @@ struct partition {
static int guess_disk_lchs(BlockDriverState *bs,
int *pcylinders, int *pheads, int *psectors)
{
- uint8_t buf[512];
+ uint8_t *buf;
int ret, i, heads, sectors, cylinders;
struct partition *p;
uint32_t nr_sects;
uint64_t nb_sectors;
+ buf = qemu_memalign(512, 512);
+ if (buf == NULL)
+ return -1;
+
bdrv_get_geometry(bs, &nb_sectors);
ret = bdrv_read(bs, 0, buf, 1);
if (ret < 0)
return -1;
/* test msdos magic */
- if (buf[510] != 0x55 || buf[511] != 0xaa)
+ if (buf[510] != 0x55 || buf[511] != 0xaa) {
+ qemu_free(buf);
return -1;
+ }
for(i = 0; i < 4; i++) {
p = ((struct partition *)(buf + 0x1be)) + i;
nr_sects = le32_to_cpu(p->nr_sects);
@@ -795,9 +802,11 @@ static int guess_disk_lchs(BlockDriverState *bs,
printf("guessed geometry: LCHS=%d %d %d\n",
cylinders, heads, sectors);
#endif
+ qemu_free(buf);
return 0;
}
}
+ qemu_free(buf);
return -1;
}
@@ -995,6 +1004,16 @@ 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)
{
BlockDriverState *bs;
diff --git a/block_int.h b/block_int.h
index e83fd2c07..1fa660a24 100644
--- a/block_int.h
+++ b/block_int.h
@@ -126,6 +126,8 @@ struct BlockDriverState {
int cyls, heads, secs, translation;
int type;
char device_name[32];
+ /* PCI devfn of parent */
+ int devfn;
BlockDriverState *next;
};
diff --git a/compatfd.c b/compatfd.c
new file mode 100644
index 000000000..36e37e5d1
--- /dev/null
+++ b/compatfd.c
@@ -0,0 +1,131 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "compatfd.h"
+
+#include <sys/syscall.h>
+#include <pthread.h>
+
+struct sigfd_compat_info
+{
+ sigset_t mask;
+ int fd;
+};
+
+static void *sigwait_compat(void *opaque)
+{
+ struct sigfd_compat_info *info = opaque;
+ int err;
+ sigset_t all;
+
+ sigfillset(&all);
+ sigprocmask(SIG_BLOCK, &all, NULL);
+
+ do {
+ siginfo_t siginfo;
+
+ err = sigwaitinfo(&info->mask, &siginfo);
+ if (err == -1 && errno == EINTR) {
+ err = 0;
+ continue;
+ }
+
+ if (err > 0) {
+ char buffer[128];
+ size_t offset = 0;
+
+ memcpy(buffer, &err, sizeof(err));
+ while (offset < sizeof(buffer)) {
+ ssize_t len;
+
+ len = write(info->fd, buffer + offset,
+ sizeof(buffer) - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0) {
+ err = -1;
+ break;
+ }
+
+ offset += len;
+ }
+ }
+ } while (err >= 0);
+
+ return NULL;
+}
+
+static int qemu_signalfd_compat(const sigset_t *mask)
+{
+ pthread_attr_t attr;
+ pthread_t tid;
+ struct sigfd_compat_info *info;
+ int fds[2];
+
+ info = malloc(sizeof(*info));
+ if (info == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (pipe(fds) == -1) {
+ free(info);
+ return -1;
+ }
+
+ memcpy(&info->mask, mask, sizeof(*mask));
+ info->fd = fds[1];
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_create(&tid, &attr, sigwait_compat, info);
+
+ pthread_attr_destroy(&attr);
+
+ return fds[0];
+}
+
+int qemu_signalfd(const sigset_t *mask)
+{
+#if defined(CONFIG_signalfd)
+ int ret;
+
+ ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
+ if (ret != -1)
+ return ret;
+#endif
+
+ return qemu_signalfd_compat(mask);
+}
+
+int qemu_eventfd(int *fds)
+{
+#if defined(CONFIG_eventfd)
+ int ret;
+
+ ret = syscall(SYS_eventfd, 0);
+ if (ret >= 0) {
+ fds[0] = ret;
+ if ((fds[1] = dup(ret)) == -1) {
+ close(ret);
+ return -1;
+ }
+ return 0;
+ }
+#endif
+
+ return pipe(fds);
+}
diff --git a/compatfd.h b/compatfd.h
new file mode 100644
index 000000000..55a111a57
--- /dev/null
+++ b/compatfd.h
@@ -0,0 +1,28 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COMPATFD_H
+#define QEMU_COMPATFD_H
+
+#include <signal.h>
+
+struct qemu_signalfd_siginfo {
+ uint32_t ssi_signo;
+ uint8_t pad[124];
+};
+
+int qemu_signalfd(const sigset_t *mask);
+
+int qemu_eventfd(int *fds);
+
+#endif
diff --git a/configure b/configure
index ec4b229f5..d7dd10647 100755
--- a/configure
+++ b/configure
@@ -116,10 +116,15 @@ nptl="yes"
mixemu="no"
bluez="yes"
kvm="yes"
+kvm_cap_pit="no"
+kvm_cap_device_assignment="no"
kerneldir=""
aix="no"
blobs="yes"
fdt="yes"
+signalfd="no"
+eventfd="no"
+cpu_emulation="yes"
# OS specific
targetos=`uname -s`
@@ -152,6 +157,7 @@ audio_drv_list="oss"
audio_possible_drivers="oss sdl esd pa"
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
kqemu="yes"
+ kvm="yes"
fi
;;
NetBSD)
@@ -223,6 +229,16 @@ usb="linux"
if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
kqemu="yes"
audio_possible_drivers="$audio_possible_drivers fmod"
+ kvm="yes"
+fi
+if [ "$cpu" = "ia64" ] ; then
+ kvm="yes"
+ cpu_emulation="no"
+ gdbstub="no"
+ slirp="no"
+fi
+if [ "$cpu" = "powerpc" ]; then
+ kvm="yes"
fi
;;
esac
@@ -371,7 +387,9 @@ for opt do
;;
--kerneldir=*) kerneldir="$optarg"
;;
- *) echo "ERROR: unknown option $opt"; show_help="yes"
+ --disable-cpu-emulation) cpu_emulation="no"
+ ;;
+ *) echo "ERROR: unknown option $opt"; exit 1
;;
esac
done
@@ -482,6 +500,7 @@ echo " --disable-vde disable support for vde network"
echo " --disable-aio disable AIO support"
echo " --disable-blobs disable installing provided firmware blobs"
echo " --kerneldir=PATH look for kernel includes in PATH"
+echo " --disable-cpu-emulation disables use of qemu cpu emulation code"
echo ""
echo "NOTE: The object files are built at the place where configure is launched"
exit 1
@@ -696,6 +715,38 @@ else
fi
##########################################
+# KVM probe
+
+if test "$kvm" = "yes" ; then
+
+# test for KVM_CAP_PIT
+
+cat > $TMPC <<EOF
+#include <libkvm.h>
+#ifndef KVM_CAP_PIT
+#error "kvm no pit capability"
+#endif
+int main(void) { return 0; }
+EOF
+ if $cc $ARCH_CFLAGS $CFLAGS -I"$kerneldir"/include -o $TMPE ${OS_CFLAGS} $TMPC 2> /dev/null ; then
+ kvm_cap_pit="yes"
+ fi
+
+# test for KVM_CAP_DEVICE_ASSIGNMENT
+
+cat > $TMPC <<EOF
+#include <libkvm.h>
+#ifndef KVM_CAP_DEVICE_ASSIGNMENT
+#error "kvm no device assignment capability"
+#endif
+int main(void) { return 0; }
+EOF
+ if $cc $ARCH_CFLAGS $CFLAGS -I"$kerneldir"/include -o $TMPE ${OS_CFLAGS} $TMPC 2> /dev/null ; then
+ kvm_cap_device_assignment="yes"
+ fi
+fi
+
+##########################################
# zlib check
cat > $TMPC << EOF
@@ -979,6 +1030,33 @@ EOF
fi
fi
+##########################################
+# signalfd probe
+cat > $TMPC << EOF
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <signal.h>
+int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); }
+EOF
+
+if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then
+ signalfd=yes
+fi
+
+##########################################
+# eventfd probe
+cat > $TMPC << EOF
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/syscall.h>
+int main(void) { return syscall(SYS_eventfd, 0); }
+EOF
+
+if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then
+ eventfd=yes
+fi
+
# Check if tools are available to build documentation.
if [ -x "`which texi2html 2>/dev/null`" ] && \
[ -x "`which pod2man 2>/dev/null`" ]; then
@@ -1065,6 +1143,8 @@ if test -n "$sparc_cpu"; then
echo "Target Sparc Arch $sparc_cpu"
fi
echo "kqemu support $kqemu"
+echo "kvm support $kvm"
+echo "CPU emulation $cpu_emulation"
echo "brlapi support $brlapi"
echo "Documentation $build_docs"
[ ! -z "$uname_release" ] && \
@@ -1306,6 +1386,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
@@ -1368,6 +1449,12 @@ if test "$fdt" = "yes" ; then
echo "#define HAVE_FDT 1" >> $config_h
echo "FDT_LIBS=-lfdt" >> $config_mak
fi
+if test "$signalfd" = "yes" ; then
+ echo "#define CONFIG_signalfd 1" >> $config_h
+fi
+if test "$eventfd" = "yes" ; then
+ echo "#define CONFIG_eventfd 1" >> $config_h
+fi
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
@@ -1489,6 +1576,33 @@ interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_cpu/g"`
echo "#define CONFIG_QEMU_PREFIX \"$interp_prefix1\"" >> $config_h
gdb_xml_files=""
+disable_cpu_emulation() {
+ if test $cpu_emulation = "no"; then
+ echo "#define NO_CPU_EMULATION 1" >> $config_h
+ echo "NO_CPU_EMULATION=1" >> $config_mak
+ fi
+}
+
+configure_kvm() {
+ if test $kvm = "yes" -a "$target_softmmu" = "yes" -a \
+ \( "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "ia64" -o "$cpu" = "powerpc" \); then
+ echo "#define USE_KVM 1" >> $config_h
+ echo "USE_KVM=1" >> $config_mak
+ echo "CONFIG_KVM_KERNEL_INC=$kerneldir/include" >> $config_mak
+ if test $kvm_cap_pit = "yes" ; then
+ echo "USE_KVM_PIT=1" >> $config_mak
+ echo "#define USE_KVM_PIT 1" >> $config_h
+ fi
+ if test $kvm_cap_device_assignment = "yes" ; then
+ echo "USE_KVM_DEVICE_ASSIGNMENT=1" >> $config_mak
+ echo "#define USE_KVM_DEVICE_ASSIGNMENT 1" >> $config_h
+ fi
+ disable_cpu_emulation
+ fi
+}
+
+if [ use_upstream_kvm = yes ]; then
+
# Make sure the target and host cpus are compatible
if test "$kvm" = "yes" -a ! \( "$target_cpu" = "$cpu" -o \
\( "$target_cpu" = "ppcemb" -a "$cpu" = "powerpc" \) -o \
@@ -1501,6 +1615,8 @@ if test "$kvm" = "yes" -a "$target_softmmu" = "no" ; then
kvm="no"
fi
+fi
+
case "$target_cpu" in
i386)
echo "TARGET_ARCH=i386" >> $config_mak
@@ -1515,6 +1631,7 @@ case "$target_cpu" in
echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak
echo "#define CONFIG_KVM 1" >> $config_h
fi
+ configure_kvm
;;
x86_64)
echo "TARGET_ARCH=x86_64" >> $config_mak
@@ -1525,11 +1642,20 @@ case "$target_cpu" in
then
echo "#define USE_KQEMU 1" >> $config_h
fi
+ configure_kvm
+ ;;
+ ia64)
+ echo "TARGET_ARCH=ia64" >> $config_mak
+ echo "#define TARGET_ARCH \"ia64\"" >> $config_h
+ echo "#define TARGET_IA64 1" >> $config_h
+ configure_kvm
+ if [ use_upstream_kvm = yes ]; then
if test "$kvm" = "yes" ; then
echo "CONFIG_KVM=yes" >> $config_mak
echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak
echo "#define CONFIG_KVM 1" >> $config_h
fi
+ fi
;;
alpha)
echo "TARGET_ARCH=alpha" >> $config_mak
diff --git a/cpu-all.h b/cpu-all.h
index 648264cff..c512f9888 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -856,6 +856,7 @@ extern int phys_ram_fd;
extern uint8_t *phys_ram_base;
extern uint8_t *phys_ram_dirty;
extern ram_addr_t ram_size;
+extern uint8_t *bios_mem;
/* physical memory access */
@@ -907,6 +908,7 @@ int cpu_register_io_memory(int io_index,
CPUReadMemoryFunc **mem_read,
CPUWriteMemoryFunc **mem_write,
void *opaque);
+void cpu_unregister_io_memory(int table_address);
CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index);
CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index);
diff --git a/cpu-defs.h b/cpu-defs.h
index ed8c00146..6ca84b2ef 100644
--- a/cpu-defs.h
+++ b/cpu-defs.h
@@ -27,6 +27,7 @@
#include "config.h"
#include <setjmp.h>
#include <inttypes.h>
+#include <pthread.h>
#include "osdep.h"
#include "sys-queue.h"
@@ -157,6 +158,20 @@ typedef struct CPUWatchpoint {
TAILQ_ENTRY(CPUWatchpoint) entry;
} CPUWatchpoint;
+/* forward decleration */
+struct qemu_work_item;
+
+struct KVMCPUState {
+ int sipi_needed;
+ int init;
+ pthread_t thread;
+ int signalled;
+ int stop;
+ int stopped;
+ int created;
+ struct qemu_work_item *queued_work_first, *queued_work_last;
+};
+
#define CPU_TEMP_BUF_NLONGS 128
#define CPU_COMMON \
struct TranslationBlock *current_tb; /* currently executing TB */ \
@@ -206,12 +221,14 @@ typedef struct CPUWatchpoint {
void *next_cpu; /* next CPU sharing TB cache */ \
int cpu_index; /* CPU index (informative) */ \
int running; /* Nonzero if cpu is currently running(usermode). */ \
+ int thread_id; \
/* user data */ \
void *opaque; \
\
const char *cpu_model_str; \
struct KVMState *kvm_state; \
struct kvm_run *kvm_run; \
- int kvm_fd;
+ int kvm_fd; \
+ struct KVMCPUState kvm_cpu_state;
#endif
diff --git a/cpu-exec.c b/cpu-exec.c
index aaf6114ef..447cd93b2 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -21,7 +21,9 @@
#define CPU_NO_GLOBAL_REGS
#include "exec.h"
#include "disas.h"
+#if !defined(TARGET_IA64)
#include "tcg.h"
+#endif
#include "kvm.h"
#if !defined(CONFIG_SOFTMMU)
@@ -40,6 +42,8 @@
#endif
#endif
+#include "qemu-kvm.h"
+
#if defined(__sparc__) && !defined(HOST_SOLARIS)
// Work around ugly bugs in glibc that mangle global register contents
#undef env
@@ -245,6 +249,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
@@ -304,6 +309,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;
@@ -334,6 +341,12 @@ int cpu_exec(CPUState *env1)
}
#endif
+ /* kvm vcpu threads */
+ if (kvm_enabled()) {
+ kvm_cpu_exec(env);
+ longjmp(env->jmp_env, 1);
+ }
+
if (kvm_enabled()) {
kvm_cpu_exec(env);
longjmp(env->jmp_env, 1);
@@ -673,6 +686,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 9617e083c..280d0b168 100644
--- a/cutils.c
+++ b/cutils.c
@@ -97,6 +97,45 @@ time_t mktimegm(struct tm *tm)
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;
+}
+
int qemu_fls(int i)
{
return 32 - clz32(i);
diff --git a/exec.c b/exec.c
index 44f6a4255..f3646fe76 100644
--- a/exec.c
+++ b/exec.c
@@ -36,7 +36,12 @@
#include "cpu.h"
#include "exec-all.h"
#include "qemu-common.h"
+
+#if !defined(TARGET_IA64)
#include "tcg.h"
+#endif
+#include "qemu-kvm.h"
+
#include "hw/hw.h"
#include "osdep.h"
#include "kvm.h"
@@ -79,6 +84,8 @@
#define TARGET_PHYS_ADDR_SPACE_BITS 42
#elif defined(TARGET_I386) && !defined(USE_KQEMU)
#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#elif defined(TARGET_IA64)
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
#else
/* Note: for compatibility with kqemu, we use 32 bits for x86_64 */
#define TARGET_PHYS_ADDR_SPACE_BITS 32
@@ -115,6 +122,7 @@ 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;
#endif
@@ -179,7 +187,7 @@ static void io_mem_init(void);
CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
void *io_mem_opaque[IO_MEM_NB_ENTRIES];
-static int io_mem_nb;
+char io_mem_used[IO_MEM_NB_ENTRIES];
static int io_mem_watch;
#endif
@@ -401,6 +409,9 @@ static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE];
static void code_gen_alloc(unsigned long tb_size)
{
+ if (kvm_enabled())
+ return;
+
#ifdef USE_STATIC_CODE_GEN_BUFFER
code_gen_buffer = static_code_gen_buffer;
code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
@@ -546,6 +557,11 @@ void cpu_exec_init(CPUState *env)
env->cpu_index = cpu_index;
TAILQ_INIT(&env->breakpoints);
TAILQ_INIT(&env->watchpoints);
+#ifdef __WIN32
+ env->thread_id = GetCurrentProcessId();
+#else
+ env->thread_id = getpid();
+#endif
*penv = env;
#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
register_savevm("cpu_common", cpu_index, CPU_COMMON_SAVE_VERSION,
@@ -1458,9 +1474,13 @@ void cpu_single_step(CPUState *env, int enabled)
#if defined(TARGET_HAS_ICE)
if (env->singlestep_enabled != enabled) {
env->singlestep_enabled = enabled;
- /* must flush all the translated code to avoid inconsistancies */
- /* XXX: only flush what is necessary */
- tb_flush(env);
+ if (kvm_enabled())
+ kvm_update_guest_debug(env, 0);
+ else {
+ /* must flush all the translated code to avoid inconsistancies */
+ /* XXX: only flush what is necessary */
+ tb_flush(env);
+ }
}
#endif
}
@@ -1515,6 +1535,8 @@ void cpu_interrupt(CPUState *env, int mask)
/* FIXME: This is probably not threadsafe. A different thread could
be in the middle of a read-modify-write operation. */
env->interrupt_request |= mask;
+ if (kvm_enabled() && !qemu_kvm_irqchip_in_kernel())
+ kvm_update_interrupt_request(env);
#if defined(USE_NPTL)
/* FIXME: TB unchaining isn't SMP safe. For now just ignore the
problem and hope the cpu will stop of its own accord. For userspace
@@ -1849,8 +1871,12 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
int cpu_physical_memory_set_dirty_tracking(int enable)
{
+ int r=0;
+
+ if (kvm_enabled())
+ r = kvm_physical_memory_set_dirty_tracking(enable);
in_migration = enable;
- return 0;
+ return r;
}
int cpu_physical_memory_get_dirty_tracking(void)
@@ -2788,12 +2814,28 @@ static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys,
return mmio;
}
+static int get_free_io_mem_idx(void)
+{
+ int i;
+
+ for (i = 0; i<IO_MEM_NB_ENTRIES; i++)
+ if (!io_mem_used[i]) {
+ io_mem_used[i] = 1;
+ return i;
+ }
+
+ return -1;
+}
+
static void io_mem_init(void)
{
+ int i;
+
cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL);
cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write, NULL);
cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL);
- io_mem_nb = 5;
+ for (i=0; i<5; i++)
+ io_mem_used[i] = 1;
io_mem_watch = cpu_register_io_memory(0, watch_mem_read,
watch_mem_write, NULL);
@@ -2818,9 +2860,9 @@ int cpu_register_io_memory(int io_index,
int i, subwidth = 0;
if (io_index <= 0) {
- if (io_mem_nb >= IO_MEM_NB_ENTRIES)
- return -1;
- io_index = io_mem_nb++;
+ io_index = get_free_io_mem_idx();
+ if (io_index == -1)
+ return io_index;
} else {
if (io_index >= IO_MEM_NB_ENTRIES)
return -1;
@@ -2836,6 +2878,19 @@ int cpu_register_io_memory(int io_index,
return (io_index << IO_MEM_SHIFT) | subwidth;
}
+void cpu_unregister_io_memory(int io_table_address)
+{
+ int i;
+ int io_index = io_table_address >> IO_MEM_SHIFT;
+
+ for (i=0;i < 3; i++) {
+ io_mem_read[io_index][i] = unassigned_mem_read[i];
+ io_mem_write[io_index][i] = unassigned_mem_write[i];
+ }
+ io_mem_opaque[io_index] = NULL;
+ io_mem_used[io_index] = 0;
+}
+
CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index)
{
return io_mem_write[io_index >> IO_MEM_SHIFT];
@@ -2949,6 +3004,11 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
(0xff & ~CODE_DIRTY_FLAG);
}
+ /* qemu doesn't execute guest code directly, but kvm does
+ therefore fluch instruction caches */
+ if (kvm_enabled())
+ flush_icache_range((unsigned long)ptr,
+ ((unsigned long)ptr)+l);
}
} else {
if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
@@ -3112,6 +3172,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 */
diff --git a/gdbstub.c b/gdbstub.c
index f7c065c95..4d99efd45 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
#define MAX_PACKET_LENGTH 4096
@@ -1389,13 +1390,6 @@ void gdb_register_coprocessor(CPUState * env,
}
}
-/* GDB breakpoint/watchpoint types */
-#define GDB_BREAKPOINT_SW 0
-#define GDB_BREAKPOINT_HW 1
-#define GDB_WATCHPOINT_WRITE 2
-#define GDB_WATCHPOINT_READ 3
-#define GDB_WATCHPOINT_ACCESS 4
-
#ifndef CONFIG_USER_ONLY
static const int xlat_gdb_type[] = {
[GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE,
@@ -1409,6 +1403,9 @@ static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type)
CPUState *env;
int err = 0;
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type);
+
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
@@ -1440,6 +1437,9 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type)
CPUState *env;
int err = 0;
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type);
+
switch (type) {
case GDB_BREAKPOINT_SW:
case GDB_BREAKPOINT_HW:
@@ -1469,6 +1469,11 @@ static void gdb_breakpoint_remove_all(void)
{
CPUState *env;
+ if (kvm_enabled()) {
+ kvm_remove_all_breakpoints(gdbserver_state->c_cpu);
+ return;
+ }
+
for (env = first_cpu; env != NULL; env = env->next_cpu) {
cpu_breakpoint_remove_all(env, BP_GDB);
#ifndef CONFIG_USER_ONLY
@@ -1509,8 +1514,10 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
s->c_cpu->eip = addr;
+ kvm_load_registers(s->c_cpu);
#elif defined (TARGET_PPC)
s->c_cpu->nip = addr;
+ kvm_load_registers(s->c_cpu);
#elif defined (TARGET_SPARC)
s->c_cpu->pc = addr;
s->c_cpu->npc = addr + 4;
@@ -1550,8 +1557,10 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
addr = strtoull(p, (char **)&p, 16);
#if defined(TARGET_I386)
s->c_cpu->eip = addr;
+ kvm_load_registers(s->c_cpu);
#elif defined (TARGET_PPC)
s->c_cpu->nip = addr;
+ kvm_load_registers(s->c_cpu);
#elif defined (TARGET_SPARC)
s->c_cpu->pc = addr;
s->c_cpu->npc = addr + 4;
@@ -1595,6 +1604,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
}
break;
case 'g':
+ kvm_save_registers(s->g_cpu);
len = 0;
for (addr = 0; addr < num_g_regs; addr++) {
reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr);
@@ -1612,6 +1622,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
len -= reg_size;
registers += reg_size;
}
+ kvm_load_registers(s->g_cpu);
put_packet(s, "OK");
break;
case 'm':
@@ -1770,6 +1781,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
thread = strtoull(p+16, (char **)&p, 16);
for (env = first_cpu; env != NULL; env = env->next_cpu)
if (env->cpu_index + 1 == thread) {
+ kvm_save_registers(env);
len = snprintf((char *)mem_buf, sizeof(mem_buf),
"CPU#%d [%s]", env->cpu_index,
env->halted ? "halted " : "running");
diff --git a/gdbstub.h b/gdbstub.h
index c7d1c4b43..5740041c7 100644
--- a/gdbstub.h
+++ b/gdbstub.h
@@ -3,6 +3,13 @@
#define DEFAULT_GDBSTUB_PORT "1234"
+/* GDB breakpoint/watchpoint types */
+#define GDB_BREAKPOINT_SW 0
+#define GDB_BREAKPOINT_HW 1
+#define GDB_WATCHPOINT_WRITE 2
+#define GDB_WATCHPOINT_READ 3
+#define GDB_WATCHPOINT_ACCESS 4
+
typedef void (*gdb_syscall_complete_cb)(CPUState *env,
target_ulong ret, target_ulong err);
diff --git a/hw/acpi.c b/hw/acpi.c
index dd9100aa4..0ff8851b5 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -24,6 +24,8 @@
#include "i2c.h"
#include "smbus.h"
#include "kvm.h"
+#include "qemu-kvm.h"
+#include "string.h"
//#define DEBUG
@@ -518,6 +520,13 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
pci_conf[0x40] = 0x01; /* PM io base read only bit */
+#if defined(TARGET_IA64)
+ pci_conf[0x40] = 0x41; /* PM io base read only bit */
+ pci_conf[0x41] = 0x1f;
+ pm_write_config(s, 0x80, 0x01, 1); /*Set default pm_io_base 0x1f40*/
+ s->pmcntrl = SCI_EN;
+#endif
+
register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s);
register_ioport_read(0xb2, 2, 1, pm_smi_readb, s);
@@ -564,3 +573,253 @@ void qemu_system_powerdown(void)
}
}
#endif
+#define GPE_BASE 0xafe0
+#define PROC_BASE 0xaf00
+#define PCI_BASE 0xae00
+#define PCI_EJ_BASE 0xae08
+
+struct gpe_regs {
+ uint16_t sts; /* status */
+ uint16_t en; /* enabled */
+ uint8_t up;
+ uint8_t down;
+};
+
+struct pci_status {
+ uint32_t up;
+ uint32_t down;
+};
+
+static struct gpe_regs gpe;
+static struct pci_status pci0_status;
+
+static uint32_t gpe_readb(void *opaque, uint32_t addr)
+{
+ uint32_t val = 0;
+ struct gpe_regs *g = opaque;
+ switch (addr) {
+ case PROC_BASE:
+ val = g->up;
+ break;
+ case PROC_BASE + 1:
+ val = g->down;
+ break;
+
+ case GPE_BASE:
+ val = g->sts & 0xFF;
+ break;
+ case GPE_BASE + 1:
+ val = (g->sts >> 8) & 0xFF;
+ break;
+ case GPE_BASE + 2:
+ val = g->en & 0xFF;
+ break;
+ case GPE_BASE + 3:
+ val = (g->en >> 8) & 0xFF;
+ break;
+ default:
+ break;
+ }
+
+#if defined(DEBUG)
+ printf("gpe read %x == %x\n", addr, val);
+#endif
+ return val;
+}
+
+static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ struct gpe_regs *g = opaque;
+ switch (addr) {
+ case PROC_BASE:
+ g->up = val;
+ break;
+ case PROC_BASE + 1:
+ g->down = val;
+ break;
+
+ case GPE_BASE:
+ g->sts = (g->sts & ~0xFFFF) | (val & 0xFFFF);
+ break;
+ case GPE_BASE + 1:
+ g->sts = (g->sts & 0xFFFF) | (val << 8);
+ break;
+ case GPE_BASE + 2:
+ g->en = (g->en & ~0xFFFF) | (val & 0xFFFF);
+ break;
+ case GPE_BASE + 3:
+ g->en = (g->en & 0xFFFF) | (val << 8);
+ break;
+ default:
+ break;
+ }
+
+#if defined(DEBUG)
+ printf("gpe write %x <== %d\n", addr, val);
+#endif
+}
+
+static uint32_t pcihotplug_read(void *opaque, uint32_t addr)
+{
+ uint32_t val = 0;
+ struct pci_status *g = opaque;
+ switch (addr) {
+ case PCI_BASE:
+ val = g->up;
+ break;
+ case PCI_BASE + 4:
+ val = g->down;
+ break;
+ default:
+ break;
+ }
+
+#if defined(DEBUG)
+ printf("pcihotplug read %x == %x\n", addr, val);
+#endif
+ return val;
+}
+
+static void pcihotplug_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ struct pci_status *g = opaque;
+ switch (addr) {
+ case PCI_BASE:
+ g->up = val;
+ break;
+ case PCI_BASE + 4:
+ g->down = val;
+ break;
+ }
+
+#if defined(DEBUG)
+ printf("pcihotplug write %x <== %d\n", addr, val);
+#endif
+}
+
+static uint32_t pciej_read(void *opaque, uint32_t addr)
+{
+#if defined(DEBUG)
+ printf("pciej read %x\n", addr);
+#endif
+ return 0;
+}
+
+static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ int slot = ffs(val) - 1;
+
+ device_hot_remove_success(0, slot);
+
+#if defined(DEBUG)
+ printf("pciej write %x <== %d\n", addr, val);
+#endif
+}
+
+static const char *model;
+
+void qemu_system_hot_add_init(const char *cpu_model)
+{
+ register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, &gpe);
+ register_ioport_read(GPE_BASE, 4, 1, gpe_readb, &gpe);
+
+ register_ioport_write(PROC_BASE, 4, 1, gpe_writeb, &gpe);
+ register_ioport_read(PROC_BASE, 4, 1, gpe_readb, &gpe);
+
+ register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, &pci0_status);
+ register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, &pci0_status);
+
+ register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, NULL);
+ register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, NULL);
+
+ model = cpu_model;
+}
+
+static void enable_processor(struct gpe_regs *g, int cpu)
+{
+ g->sts |= 1;
+ g->en |= 1;
+ g->up |= (1 << cpu);
+}
+
+static void disable_processor(struct gpe_regs *g, int cpu)
+{
+ g->sts |= 1;
+ g->en |= 1;
+ g->down |= (1 << cpu);
+}
+
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+#ifdef USE_KVM
+static CPUState *qemu_kvm_cpu_env(int index)
+{
+ CPUState *penv;
+
+ penv = first_cpu;
+
+ while (penv) {
+ if (penv->cpu_index == index)
+ return penv;
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ return NULL;
+}
+#endif
+
+
+void qemu_system_cpu_hot_add(int cpu, int state)
+{
+ CPUState *env;
+
+ if (state
+#ifdef USE_KVM
+ && (!qemu_kvm_cpu_env(cpu))
+#endif
+ ) {
+ env = pc_new_cpu(cpu, model, 1);
+ if (!env) {
+ fprintf(stderr, "cpu %d creation failed\n", cpu);
+ return;
+ }
+#ifdef USE_KVM
+ kvm_init_vcpu(env);
+#endif
+ }
+
+ qemu_set_irq(pm_state->irq, 1);
+ gpe.up = 0;
+ gpe.down = 0;
+ if (state)
+ enable_processor(&gpe, cpu);
+ else
+ disable_processor(&gpe, cpu);
+ qemu_set_irq(pm_state->irq, 0);
+}
+#endif
+
+static void enable_device(struct pci_status *p, struct gpe_regs *g, int slot)
+{
+ g->sts |= 2;
+ g->en |= 2;
+ p->up |= (1 << slot);
+}
+
+static void disable_device(struct pci_status *p, struct gpe_regs *g, int slot)
+{
+ g->sts |= 2;
+ g->en |= 2;
+ p->down |= (1 << slot);
+}
+
+void qemu_system_device_hot_add(int pcibus, int slot, int state)
+{
+ qemu_set_irq(pm_state->irq, 1);
+ pci0_status.up = 0;
+ pci0_status.down = 0;
+ if (state)
+ enable_device(&pci0_status, &gpe, slot);
+ else
+ disable_device(&pci0_status, &gpe, slot);
+ qemu_set_irq(pm_state->irq, 0);
+}
diff --git a/hw/apic.c b/hw/apic.c
index f6950ca4d..eb4d51992 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -22,6 +22,8 @@
#include "qemu-timer.h"
#include "host-utils.h"
+#include "qemu-kvm.h"
+
//#define DEBUG_APIC
//#define DEBUG_IOAPIC
@@ -57,7 +59,9 @@
#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 IOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000
#define ESR_ILLEGAL_ADDRESS (1 << 7)
@@ -92,6 +96,7 @@ typedef struct APICState {
struct IOAPICState {
uint8_t id;
uint8_t ioregsel;
+ uint64_t base_address;
uint32_t irr;
uint64_t ioredtbl[IOAPIC_NUM_PINS];
@@ -268,8 +273,11 @@ void cpu_set_apic_base(CPUState *env, uint64_t val)
#ifdef DEBUG_APIC
printf("cpu_set_apic_base: %016" PRIx64 "\n", val);
#endif
- s->apicbase = (val & 0xfffff000) |
- (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel())
+ s->apicbase = val;
+ else
+ s->apicbase = (val & 0xfffff000) |
+ (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
/* if disabled, cannot be enabled again */
if (!(val & MSR_IA32_APICBASE_ENABLE)) {
s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
@@ -428,8 +436,13 @@ static void apic_init_ipi(APICState *s)
cpu_reset(s->cpu_env);
- if (!(s->apicbase & MSR_IA32_APICBASE_BSP))
+ if (!(s->apicbase & MSR_IA32_APICBASE_BSP) &&
+ (!kvm_enabled() || !qemu_kvm_irqchip_in_kernel()))
s->cpu_env->halted = 1;
+
+ if (kvm_enabled() && !qemu_kvm_irqchip_in_kernel())
+ if (s->cpu_env)
+ kvm_apic_init(s->cpu_env);
}
/* send a SIPI message to the CPU to start it */
@@ -442,6 +455,8 @@ 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->halted = 0;
+ if (kvm_enabled() && !qemu_kvm_irqchip_in_kernel())
+ kvm_update_after_sipi(env);
}
static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode,
@@ -768,11 +783,94 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
}
+#ifdef KVM_CAP_IRQCHIP
+
+static inline uint32_t kapic_reg(struct kvm_lapic_state *kapic, int reg_id)
+{
+ return *((uint32_t *) (kapic->regs + (reg_id << 4)));
+}
+
+static inline void kapic_set_reg(struct kvm_lapic_state *kapic,
+ int reg_id, uint32_t val)
+{
+ *((uint32_t *) (kapic->regs + (reg_id << 4))) = val;
+}
+
+static void kvm_kernel_lapic_save_to_user(APICState *s)
+{
+ struct kvm_lapic_state apic;
+ struct kvm_lapic_state *kapic = &apic;
+ int i, v;
+
+ kvm_get_lapic(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
+
static void apic_save(QEMUFile *f, void *opaque)
{
APICState *s = opaque;
int i;
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_lapic_save_to_user(s);
+ }
+#endif
+
qemu_put_be32s(f, &s->apicbase);
qemu_put_8s(f, &s->id);
qemu_put_8s(f, &s->arb_id);
@@ -835,6 +933,13 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id)
if (version_id >= 2)
qemu_get_timer(f, s->timer);
+
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_lapic_load_from_user(s);
+ }
+#endif
+
return 0;
}
@@ -855,6 +960,11 @@ static void apic_reset(void *opaque)
*/
s->lvt[APIC_LVT_LINT0] = 0x700;
}
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_lapic_load_from_user(s);
+ }
+#endif
}
static CPUReadMemoryFunc *apic_mem_read[3] = {
@@ -1046,13 +1156,62 @@ static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t va
}
}
+static void kvm_kernel_ioapic_save_to_user(IOAPICState *s)
+{
+#if defined(KVM_CAP_IRQCHIP) && defined(TARGET_I386)
+ struct kvm_irqchip chip;
+ struct kvm_ioapic_state *kioapic;
+ int i;
+
+ chip.chip_id = KVM_IRQCHIP_IOAPIC;
+ kvm_get_irqchip(kvm_context, &chip);
+ kioapic = &chip.chip.ioapic;
+
+ s->id = kioapic->id;
+ s->ioregsel = kioapic->ioregsel;
+ s->base_address = kioapic->base_address;
+ s->irr = kioapic->irr;
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ s->ioredtbl[i] = kioapic->redirtbl[i].bits;
+ }
+#endif
+}
+
+static void kvm_kernel_ioapic_load_from_user(IOAPICState *s)
+{
+#if defined(KVM_CAP_IRQCHIP) && defined(TARGET_I386)
+ struct kvm_irqchip chip;
+ struct kvm_ioapic_state *kioapic;
+ int i;
+
+ chip.chip_id = KVM_IRQCHIP_IOAPIC;
+ kioapic = &chip.chip.ioapic;
+
+ kioapic->id = s->id;
+ kioapic->ioregsel = s->ioregsel;
+ kioapic->base_address = s->base_address;
+ kioapic->irr = s->irr;
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ kioapic->redirtbl[i].bits = s->ioredtbl[i];
+ }
+
+ kvm_set_irqchip(kvm_context, &chip);
+#endif
+}
+
static void ioapic_save(QEMUFile *f, void *opaque)
{
IOAPICState *s = opaque;
int i;
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_ioapic_save_to_user(s);
+ }
+
qemu_put_8s(f, &s->id);
qemu_put_8s(f, &s->ioregsel);
+ qemu_put_be64s(f, &s->base_address);
+ qemu_put_be32s(f, &s->irr);
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
qemu_put_be64s(f, &s->ioredtbl[i]);
}
@@ -1063,14 +1222,29 @@ static int ioapic_load(QEMUFile *f, void *opaque, int version_id)
IOAPICState *s = opaque;
int i;
- if (version_id != 1)
+ if (version_id < 1 || version_id > 2)
return -EINVAL;
qemu_get_8s(f, &s->id);
qemu_get_8s(f, &s->ioregsel);
+ if (version_id == 2) {
+ /* for version 2, we get this data off of the wire */
+ qemu_get_be64s(f, &s->base_address);
+ qemu_get_be32s(f, &s->irr);
+ }
+ else {
+ /* in case we are doing version 1, we just set these to sane values */
+ s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS;
+ s->irr = 0;
+ }
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
qemu_get_be64s(f, &s->ioredtbl[i]);
}
+
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_ioapic_load_from_user(s);
+ }
+
return 0;
}
@@ -1080,8 +1254,14 @@ static void ioapic_reset(void *opaque)
int i;
memset(s, 0, sizeof(*s));
+ s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS;
for(i = 0; i < IOAPIC_NUM_PINS; i++)
s->ioredtbl[i] = 1 << 16; /* mask LVT */
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_ioapic_load_from_user(s);
+ }
+#endif
}
static CPUReadMemoryFunc *ioapic_mem_read[3] = {
@@ -1111,7 +1291,7 @@ IOAPICState *ioapic_init(void)
ioapic_mem_write, s);
cpu_register_physical_memory(0xfec00000, 0x1000, io_memory);
- register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s);
+ register_savevm("ioapic", 0, 2, ioapic_save, ioapic_load, s);
qemu_register_reset(ioapic_reset, s);
return s;
diff --git a/hw/boards.h b/hw/boards.h
index bff1cf06c..d2b26c695 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -25,6 +25,8 @@ typedef struct QEMUMachine {
int qemu_register_machine(QEMUMachine *m);
void register_machines(void);
+extern QEMUMachine *current_machine;
+
/* Axis ETRAX. */
extern QEMUMachine bareetraxfs_machine;
@@ -124,4 +126,7 @@ extern QEMUMachine musicpal_machine;
/* tosa.c */
extern QEMUMachine tosapda_machine;
+/* ipf.c */
+extern QEMUMachine ipf_machine;
+
#endif
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 83c5f40d0..8d5789062 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -32,6 +32,7 @@
#include "console.h"
#include "vga_int.h"
#include "kvm.h"
+#include "qemu-kvm.h"
/*
* TODO:
@@ -861,11 +862,17 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
static void cirrus_bitblt_reset(CirrusVGAState * s)
{
+ int need_update;
+
s->gr[0x31] &=
~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED);
+ need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0]
+ || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0];
s->cirrus_srcptr = &s->cirrus_bltbuf[0];
s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
s->cirrus_srccounter = 0;
+ if (!need_update)
+ return;
cirrus_update_memory_access(s);
}
@@ -1392,6 +1399,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);
@@ -2632,6 +2641,7 @@ static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = {
static void map_linear_vram(CirrusVGAState *s)
{
+ vga_dirty_log_stop((VGAState *)s);
if (!s->map_addr && s->lfb_addr && s->lfb_end) {
s->map_addr = s->lfb_addr;
s->map_end = s->lfb_end;
@@ -2642,8 +2652,13 @@ static void map_linear_vram(CirrusVGAState *s)
if (!s->map_addr)
return;
+#ifndef TARGET_IA64
s->lfb_vram_mapped = 0;
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000,
+ (s->vram_offset + s->cirrus_bank_base[0]) | IO_MEM_UNASSIGNED);
+ cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000,
+ (s->vram_offset + s->cirrus_bank_base[1]) | IO_MEM_UNASSIGNED);
if (!(s->cirrus_srcptr != s->cirrus_srcptr_end)
&& !((s->sr[0x07] & 0x01) == 0)
&& !((s->gr[0x0B] & 0x14) == 0x14)
@@ -2655,24 +2670,24 @@ static void map_linear_vram(CirrusVGAState *s)
(s->vram_offset + s->cirrus_bank_base[1]) | IO_MEM_RAM);
s->lfb_vram_mapped = 1;
- vga_dirty_log_start((VGAState *)s);
}
else {
cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000, s->vga_io_memory);
cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000, s->vga_io_memory);
}
-
+#endif
+ vga_dirty_log_start((VGAState *)s);
}
static void unmap_linear_vram(CirrusVGAState *s)
{
- if (s->map_addr && s->lfb_addr && s->lfb_end) {
- vga_dirty_log_stop((VGAState *)s);
+ vga_dirty_log_stop((VGAState *)s);
+ if (s->map_addr && s->lfb_addr && s->lfb_end)
s->map_addr = s->map_end = 0;
- }
cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000,
s->vga_io_memory);
+ vga_dirty_log_start((VGAState *)s);
}
/* Compute the memory access functions */
@@ -3317,6 +3332,7 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int region_num,
{
CirrusVGAState *s = &((PCICirrusVGAState *)d)->cirrus_vga;
+ vga_dirty_log_stop((VGAState *)s);
/* XXX: add byte swapping apertures */
cpu_register_physical_memory(addr, s->vram_size,
s->cirrus_linear_io_addr);
@@ -3329,6 +3345,7 @@ static void cirrus_pci_lfb_map(PCIDevice *d, int region_num,
/* account for overflow */
if (s->lfb_end < addr + VGA_RAM_SIZE)
s->lfb_end = addr + VGA_RAM_SIZE;
+ vga_dirty_log_start((VGAState *)s);
}
static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
@@ -3340,6 +3357,20 @@ static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
s->cirrus_mmio_io_addr);
}
+static void pci_cirrus_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ PCICirrusVGAState *pvs = container_of(d, PCICirrusVGAState, dev);
+ CirrusVGAState *s = &pvs->cirrus_vga;
+
+ vga_dirty_log_stop((VGAState *)s);
+ pci_default_write_config(d, address, val, len);
+ if (s->map_addr && pvs->dev.io_regions[0].addr == -1)
+ s->map_addr = 0;
+ cirrus_update_memory_access(s);
+ vga_dirty_log_start((VGAState *)s);
+}
+
void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
ram_addr_t vga_ram_offset, int vga_ram_size)
{
@@ -3353,7 +3384,7 @@ void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
/* setup PCI configuration registers */
d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA",
sizeof(PCICirrusVGAState),
- -1, NULL, NULL);
+ -1, NULL, pci_cirrus_write_config);
pci_conf = d->dev.config;
pci_conf[0x00] = (uint8_t) (PCI_VENDOR_CIRRUS & 0xff);
pci_conf[0x01] = (uint8_t) (PCI_VENDOR_CIRRUS >> 8);
diff --git a/hw/device-assignment.c b/hw/device-assignment.c
new file mode 100644
index 000000000..7a666659d
--- /dev/null
+++ b/hw/device-assignment.c
@@ -0,0 +1,680 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * Assign a PCI device from the host to a guest VM.
+ *
+ * Adapted for KVM by Qumranet.
+ *
+ * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com)
+ * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com)
+ * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com)
+ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
+ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
+ */
+#include <stdio.h>
+#include <sys/io.h>
+#include "qemu-kvm.h"
+#include "hw.h"
+#include "pc.h"
+#include "sysemu.h"
+#include "console.h"
+#include "device-assignment.h"
+
+/* From linux/ioport.h */
+#define IORESOURCE_IO 0x00000100 /* Resource type */
+#define IORESOURCE_MEM 0x00000200
+#define IORESOURCE_IRQ 0x00000400
+#define IORESOURCE_DMA 0x00000800
+#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */
+
+/* #define DEVICE_ASSIGNMENT_DEBUG 1 */
+
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+#define DEBUG(fmt, ...) \
+ do { \
+ fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
+ } while (0)
+#else
+#define DEBUG(fmt, ...) do { } while(0)
+#endif
+
+static uint32_t guest_to_host_ioport(AssignedDevRegion *region, uint32_t addr)
+{
+ return region->u.r_baseport + (addr - region->e_physbase);
+}
+
+static void assigned_dev_ioport_writeb(void *opaque, uint32_t addr,
+ uint32_t value)
+{
+ AssignedDevRegion *r_access = opaque;
+ uint32_t r_pio = guest_to_host_ioport(r_access, addr);
+
+ DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n",
+ r_pio, (int)r_access->e_physbase,
+ (unsigned long)r_access->u.r_baseport, value);
+
+ outb(value, r_pio);
+}
+
+static void assigned_dev_ioport_writew(void *opaque, uint32_t addr,
+ uint32_t value)
+{
+ AssignedDevRegion *r_access = opaque;
+ uint32_t r_pio = guest_to_host_ioport(r_access, addr);
+
+ DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n",
+ r_pio, (int)r_access->e_physbase,
+ (unsigned long)r_access->u.r_baseport, value);
+
+ outw(value, r_pio);
+}
+
+static void assigned_dev_ioport_writel(void *opaque, uint32_t addr,
+ uint32_t value)
+{
+ AssignedDevRegion *r_access = opaque;
+ uint32_t r_pio = guest_to_host_ioport(r_access, addr);
+
+ DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n",
+ r_pio, (int)r_access->e_physbase,
+ (unsigned long)r_access->u.r_baseport, value);
+
+ outl(value, r_pio);
+}
+
+static uint32_t assigned_dev_ioport_readb(void *opaque, uint32_t addr)
+{
+ AssignedDevRegion *r_access = opaque;
+ uint32_t r_pio = guest_to_host_ioport(r_access, addr);
+ uint32_t value;
+
+ value = inb(r_pio);
+
+ DEBUG("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n",
+ r_pio, (int)r_access->e_physbase,
+ (unsigned long)r_access->u.r_baseport, value);
+
+ return value;
+}
+
+static uint32_t assigned_dev_ioport_readw(void *opaque, uint32_t addr)
+{
+ AssignedDevRegion *r_access = opaque;
+ uint32_t r_pio = guest_to_host_ioport(r_access, addr);
+ uint32_t value;
+
+ value = inw(r_pio);
+
+ DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n",
+ r_pio, (int)r_access->e_physbase,
+ (unsigned long)r_access->u.r_baseport, value);
+
+ return value;
+}
+
+static uint32_t assigned_dev_ioport_readl(void *opaque, uint32_t addr)
+{
+ AssignedDevRegion *r_access = opaque;
+ uint32_t r_pio = guest_to_host_ioport(r_access, addr);
+ uint32_t value;
+
+ value = inl(r_pio);
+
+ DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n",
+ r_pio, (int)r_access->e_physbase,
+ (unsigned long)r_access->u.r_baseport, value);
+
+ return value;
+}
+
+static void assigned_dev_iomem_map(PCIDevice *pci_dev, int region_num,
+ uint32_t e_phys, uint32_t e_size, int type)
+{
+ AssignedDevice *r_dev = (AssignedDevice *) pci_dev;
+ AssignedDevRegion *region = &r_dev->v_addrs[region_num];
+ uint32_t old_ephys = region->e_physbase;
+ uint32_t old_esize = region->e_size;
+ int first_map = (region->e_size == 0);
+ int ret = 0;
+
+ DEBUG("e_phys=%08x r_virt=%p type=%d len=%08x region_num=%d \n",
+ e_phys, region->u.r_virtbase, type, e_size, region_num);
+
+ region->e_physbase = e_phys;
+ region->e_size = e_size;
+
+ if (!first_map) {
+ int slot = get_slot(old_ephys);
+ if (slot != -1)
+ kvm_destroy_phys_mem(kvm_context, old_ephys,
+ TARGET_PAGE_ALIGN(old_esize));
+ }
+
+ if (e_size > 0)
+ ret = kvm_register_phys_mem(kvm_context, e_phys,
+ region->u.r_virtbase,
+ TARGET_PAGE_ALIGN(e_size), 0);
+
+ if (ret != 0) {
+ fprintf(stderr, "%s: Error: create new mapping failed\n", __func__);
+ exit(1);
+ }
+}
+
+static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ AssignedDevice *r_dev = (AssignedDevice *) pci_dev;
+ AssignedDevRegion *region = &r_dev->v_addrs[region_num];
+ int first_map = (region->e_size == 0);
+ CPUState *env;
+
+ region->e_physbase = addr;
+ region->e_size = size;
+
+ DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d \n",
+ addr, region->u.r_baseport, type, size, region_num);
+
+ if (first_map) {
+ struct ioperm_data *data;
+
+ data = qemu_mallocz(sizeof(struct ioperm_data));
+ if (data == NULL) {
+ fprintf(stderr, "%s: Out of memory\n", __func__);
+ exit(1);
+ }
+
+ data->start_port = region->u.r_baseport;
+ data->num = region->r_size;
+ data->turn_on = 1;
+
+ kvm_add_ioperm_data(data);
+
+ for (env = first_cpu; env; env = env->next_cpu)
+ kvm_ioperm(env, data);
+ }
+
+ register_ioport_read(addr, size, 1, assigned_dev_ioport_readb,
+ (r_dev->v_addrs + region_num));
+ register_ioport_read(addr, size, 2, assigned_dev_ioport_readw,
+ (r_dev->v_addrs + region_num));
+ register_ioport_read(addr, size, 4, assigned_dev_ioport_readl,
+ (r_dev->v_addrs + region_num));
+ register_ioport_write(addr, size, 1, assigned_dev_ioport_writeb,
+ (r_dev->v_addrs + region_num));
+ register_ioport_write(addr, size, 2, assigned_dev_ioport_writew,
+ (r_dev->v_addrs + region_num));
+ register_ioport_write(addr, size, 4, assigned_dev_ioport_writel,
+ (r_dev->v_addrs + region_num));
+}
+
+static void assigned_dev_pci_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ int fd;
+ ssize_t ret;
+
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7),
+ (uint16_t) address, val, len);
+
+ if (address == 0x4) {
+ pci_default_write_config(d, address, val, len);
+ /* Continue to program the card */
+ }
+
+ if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
+ address == 0x3c || address == 0x3d) {
+ /* used for update-mappings (BAR emulation) */
+ pci_default_write_config(d, address, val, len);
+ return;
+ }
+
+ DEBUG("NON BAR (%x.%x): address=%04x val=0x%08x len=%d\n",
+ ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7),
+ (uint16_t) address, val, len);
+
+ fd = ((AssignedDevice *)d)->real_device.config_fd;
+
+again:
+ ret = pwrite(fd, &val, len, address);
+ if (ret != len) {
+ if ((ret < 0) && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ fprintf(stderr, "%s: pwrite failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+}
+
+static uint32_t assigned_dev_pci_read_config(PCIDevice *d, uint32_t address,
+ int len)
+{
+ uint32_t val = 0;
+ int fd;
+ ssize_t ret;
+
+ if ((address >= 0x10 && address <= 0x24) || address == 0x34 ||
+ address == 0x3c || address == 0x3d) {
+ val = pci_default_read_config(d, address, len);
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+ return val;
+ }
+
+ /* vga specific, remove later */
+ if (address == 0xFC)
+ goto do_log;
+
+ fd = ((AssignedDevice *)d)->real_device.config_fd;
+
+again:
+ ret = pread(fd, &val, len, address);
+ if (ret != len) {
+ if ((ret < 0) && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ fprintf(stderr, "%s: pread failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+
+do_log:
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+
+ /* kill the special capabilities */
+ if (address == 4 && len == 4)
+ val &= ~0x100000;
+ else if (address == 6)
+ val &= ~0x10;
+
+ return val;
+}
+
+static int assigned_dev_register_regions(PCIRegion *io_regions,
+ unsigned long regions_num,
+ AssignedDevice *pci_dev)
+{
+ uint32_t i;
+ PCIRegion *cur_region = io_regions;
+
+ for (i = 0; i < regions_num; i++, cur_region++) {
+ if (!cur_region->valid)
+ continue;
+ pci_dev->v_addrs[i].num = i;
+
+ /* handle memory io regions */
+ if (cur_region->type & IORESOURCE_MEM) {
+ int t = cur_region->type & IORESOURCE_PREFETCH
+ ? PCI_ADDRESS_SPACE_MEM_PREFETCH
+ : PCI_ADDRESS_SPACE_MEM;
+
+ /* map physical memory */
+ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
+ pci_dev->v_addrs[i].u.r_virtbase =
+ mmap(NULL,
+ (cur_region->size + 0xFFF) & 0xFFFFF000,
+ PROT_WRITE | PROT_READ, MAP_SHARED,
+ cur_region->resource_fd, (off_t) 0);
+
+ if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) {
+ pci_dev->v_addrs[i].u.r_virtbase = NULL;
+ fprintf(stderr, "%s: Error: Couldn't mmap 0x%x!"
+ "\n", __func__,
+ (uint32_t) (cur_region->base_addr));
+ return -1;
+ }
+ pci_dev->v_addrs[i].r_size = cur_region->size;
+ pci_dev->v_addrs[i].e_size = 0;
+
+ /* add offset */
+ pci_dev->v_addrs[i].u.r_virtbase +=
+ (cur_region->base_addr & 0xFFF);
+
+ pci_register_io_region((PCIDevice *) pci_dev, i,
+ cur_region->size, t,
+ assigned_dev_iomem_map);
+ continue;
+ }
+ /* handle port io regions */
+ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
+ pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr;
+ pci_dev->v_addrs[i].r_size = cur_region->size;
+ pci_dev->v_addrs[i].e_size = 0;
+
+ pci_register_io_region((PCIDevice *) pci_dev, i,
+ cur_region->size, PCI_ADDRESS_SPACE_IO,
+ assigned_dev_ioport_map);
+
+ /* not relevant for port io */
+ pci_dev->v_addrs[i].memory_index = 0;
+ }
+
+ /* success */
+ return 0;
+}
+
+static int get_real_device(AssignedDevice *pci_dev, uint8_t r_bus,
+ uint8_t r_dev, uint8_t r_func)
+{
+ char dir[128], name[128];
+ int fd, r = 0;
+ FILE *f;
+ unsigned long long start, end, size, flags;
+ PCIRegion *rp;
+ PCIDevRegions *dev = &pci_dev->real_device;
+
+ dev->region_number = 0;
+
+ snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/0000:%02x:%02x.%x/",
+ r_bus, r_dev, r_func);
+
+ snprintf(name, sizeof(name), "%sconfig", dir);
+
+ fd = open(name, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
+ dev->config_fd = fd;
+again:
+ r = read(fd, pci_dev->dev.config, sizeof(pci_dev->dev.config));
+ if (r < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ goto again;
+ fprintf(stderr, "%s: read failed, errno = %d\n", __func__, errno);
+ }
+
+ snprintf(name, sizeof(name), "%sresource", dir);
+
+ f = fopen(name, "r");
+ if (f == NULL) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
+
+ for (r = 0; r < MAX_IO_REGIONS; r++) {
+ if (fscanf(f, "%lli %lli %lli\n", &start, &end, &flags) != 3)
+ break;
+
+ rp = dev->regions + r;
+ rp->valid = 0;
+ size = end - start + 1;
+ flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0)
+ continue;
+ if (flags & IORESOURCE_MEM) {
+ flags &= ~IORESOURCE_IO;
+ snprintf(name, sizeof(name), "%sresource%d", dir, r);
+ fd = open(name, O_RDWR);
+ if (fd == -1)
+ continue; /* probably ROM */
+ rp->resource_fd = fd;
+ } else
+ flags &= ~IORESOURCE_PREFETCH;
+
+ rp->type = flags;
+ rp->valid = 1;
+ rp->base_addr = start;
+ rp->size = size;
+ DEBUG("region %d size %d start 0x%llx type %d resource_fd %d\n",
+ r, rp->size, start, rp->type, rp->resource_fd);
+ }
+ fclose(f);
+
+ dev->region_number = r;
+ return 0;
+}
+
+static LIST_HEAD(, AssignedDevInfo) adev_head;
+
+void free_assigned_device(AssignedDevInfo *adev)
+{
+ AssignedDevice *dev = adev->assigned_dev;
+
+ if (dev) {
+ int i;
+
+ for (i = 0; i < dev->real_device.region_number; i++) {
+ PCIRegion *pci_region = &dev->real_device.regions[i];
+ AssignedDevRegion *region = &dev->v_addrs[i];
+
+ if (!pci_region->valid || !(pci_region->type & IORESOURCE_MEM))
+ continue;
+
+ if (region->u.r_virtbase) {
+ int ret = munmap(region->u.r_virtbase,
+ (pci_region->size + 0xFFF) & 0xFFFFF000);
+ if (ret != 0)
+ fprintf(stderr,
+ "Failed to unmap assigned device region: %s\n",
+ strerror(errno));
+ }
+ }
+
+ if (dev->real_device.config_fd) {
+ close(dev->real_device.config_fd);
+ dev->real_device.config_fd = 0;
+ }
+
+ pci_unregister_device(&dev->dev);
+ adev->assigned_dev = dev = NULL;
+ }
+
+ LIST_REMOVE(adev, next);
+ qemu_free(adev);
+}
+
+static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t devfn)
+{
+ return (uint32_t)bus << 8 | (uint32_t)devfn;
+}
+
+/* The pci config space got updated. Check if irq numbers have changed
+ * for our devices
+ */
+void assigned_dev_update_irq(PCIDevice *d)
+{
+ AssignedDevInfo *adev;
+
+ adev = LIST_FIRST(&adev_head);
+ while (adev) {
+ AssignedDevInfo *next = LIST_NEXT(adev, next);
+ AssignedDevice *assigned_dev = adev->assigned_dev;
+ int irq, r;
+
+ irq = pci_map_irq(&assigned_dev->dev, assigned_dev->intpin);
+ irq = piix_get_irq(irq);
+
+#ifdef TARGET_IA64
+ irq = ipf_map_irq(d, irq);
+#endif
+
+ if (irq != assigned_dev->girq) {
+ struct kvm_assigned_irq assigned_irq_data;
+
+ memset(&assigned_irq_data, 0, sizeof(assigned_irq_data));
+ assigned_irq_data.assigned_dev_id =
+ calc_assigned_dev_id(assigned_dev->h_busnr,
+ (uint8_t) assigned_dev->h_devfn);
+ assigned_irq_data.guest_irq = irq;
+ assigned_irq_data.host_irq = assigned_dev->real_device.irq;
+ r = kvm_assign_irq(kvm_context, &assigned_irq_data);
+ if (r < 0) {
+ fprintf(stderr, "Failed to assign irq for \"%s\": %s\n",
+ adev->name, strerror(-r));
+ fprintf(stderr, "Perhaps you are assigning a device "
+ "that shares an IRQ with another device?\n");
+ LIST_REMOVE(adev, next);
+ free_assigned_device(adev);
+ adev = next;
+ continue;
+ }
+ assigned_dev->girq = irq;
+ }
+
+ adev = next;
+ }
+}
+
+struct PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus)
+{
+ int r;
+ AssignedDevice *dev;
+ uint8_t e_device, e_intx;
+ struct kvm_assigned_pci_dev assigned_dev_data;
+
+ DEBUG("Registering real physical device %s (bus=%x dev=%x func=%x)\n",
+ adev->name, adev->bus, adev->dev, adev->func);
+
+ dev = (AssignedDevice *)
+ pci_register_device(bus, adev->name, sizeof(AssignedDevice),
+ -1, assigned_dev_pci_read_config,
+ assigned_dev_pci_write_config);
+ if (NULL == dev) {
+ fprintf(stderr, "%s: Error: Couldn't register real device %s\n",
+ __func__, adev->name);
+ return NULL;
+ }
+
+ adev->assigned_dev = dev;
+
+ if (get_real_device(dev, adev->bus, adev->dev, adev->func)) {
+ fprintf(stderr, "%s: Error: Couldn't get real device (%s)!\n",
+ __func__, adev->name);
+ return NULL;
+ }
+
+ /* handle real device's MMIO/PIO BARs */
+ if (assigned_dev_register_regions(dev->real_device.regions,
+ dev->real_device.region_number,
+ dev))
+ return NULL;
+
+ /* handle interrupt routing */
+ e_device = (dev->dev.devfn >> 3) & 0x1f;
+ e_intx = dev->dev.config[0x3d] - 1;
+ dev->intpin = e_intx;
+ dev->run = 0;
+ dev->girq = 0;
+ dev->h_busnr = adev->bus;
+ dev->h_devfn = PCI_DEVFN(adev->dev, adev->func);
+
+ memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+ assigned_dev_data.assigned_dev_id =
+ calc_assigned_dev_id(dev->h_busnr, (uint32_t)dev->h_devfn);
+ assigned_dev_data.busnr = dev->h_busnr;
+ assigned_dev_data.devfn = dev->h_devfn;
+
+#ifdef KVM_CAP_IOMMU
+ /* We always enable the IOMMU if present
+ * (or when not disabled on the command line)
+ */
+ r = kvm_check_extension(kvm_context, KVM_CAP_IOMMU);
+ if (r && !adev->disable_iommu)
+ assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU;
+#endif
+
+ r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
+ if (r < 0) {
+ fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
+ adev->name, strerror(-r));
+ return NULL;
+ }
+
+ return &dev->dev;
+}
+
+/*
+ * Syntax to assign device:
+ *
+ * -pcidevice host=bus:dev.func[,dma=none][,name=Foo]
+ *
+ * Example:
+ * -pcidevice host=00:13.0,dma=pvdma
+ *
+ * dma can currently only be 'none' to disable iommu support.
+ */
+AssignedDevInfo *add_assigned_device(const char *arg)
+{
+ char *cp, *cp1;
+ char device[8];
+ char dma[6];
+ int r;
+ AssignedDevInfo *adev;
+
+ adev = qemu_mallocz(sizeof(AssignedDevInfo));
+ if (adev == NULL) {
+ fprintf(stderr, "%s: Out of memory\n", __func__);
+ return NULL;
+ }
+ r = get_param_value(device, sizeof(device), "host", arg);
+ r = get_param_value(adev->name, sizeof(adev->name), "name", arg);
+ if (!r)
+ snprintf(adev->name, sizeof(adev->name), "%s", device);
+
+#ifdef KVM_CAP_IOMMU
+ r = get_param_value(dma, sizeof(dma), "dma", arg);
+ if (r && !strncmp(dma, "none", 4))
+ adev->disable_iommu = 1;
+#endif
+ cp = device;
+ adev->bus = strtoul(cp, &cp1, 16);
+ if (*cp1 != ':')
+ goto bad;
+ cp = cp1 + 1;
+
+ adev->dev = strtoul(cp, &cp1, 16);
+ if (*cp1 != '.')
+ goto bad;
+ cp = cp1 + 1;
+
+ adev->func = strtoul(cp, &cp1, 16);
+
+ LIST_INSERT_HEAD(&adev_head, adev, next);
+ return adev;
+bad:
+ fprintf(stderr, "pcidevice argument parse error; "
+ "please check the help text for usage\n");
+ qemu_free(adev);
+ return NULL;
+}
+
+void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices)
+{
+ int i;
+
+ for (i = 0; i < n_devices; i++) {
+ struct AssignedDevInfo *adev;
+
+ adev = add_assigned_device(devices[i]);
+ if (!adev) {
+ fprintf(stderr, "Could not add assigned device %s\n", devices[i]);
+ exit(1);
+ }
+
+ if (!init_assigned_device(adev, bus)) {
+ fprintf(stderr, "Failed to initialize assigned device %s\n",
+ devices[i]);
+ exit(1);
+ }
+ }
+}
diff --git a/hw/device-assignment.h b/hw/device-assignment.h
new file mode 100644
index 000000000..c8c47d31b
--- /dev/null
+++ b/hw/device-assignment.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Data structures for storing PCI state
+ *
+ * Adapted to kvm by Qumranet
+ *
+ * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com)
+ * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com)
+ * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com)
+ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
+ */
+
+#ifndef __DEVICE_ASSIGNMENT_H__
+#define __DEVICE_ASSIGNMENT_H__
+
+#include <sys/mman.h>
+#include "qemu-common.h"
+#include "sys-queue.h"
+#include "pci.h"
+
+/* From include/linux/pci.h in the kernel sources */
+#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+
+/* The number of BARs in the config space header */
+#define MAX_IO_REGIONS (6)
+
+typedef struct {
+ int type; /* Memory or port I/O */
+ int valid;
+ uint32_t base_addr;
+ uint32_t size; /* size of the region */
+ int resource_fd;
+} PCIRegion;
+
+typedef struct {
+ uint8_t bus, dev, func; /* Bus inside domain, device and function */
+ int irq; /* IRQ number */
+ uint16_t region_number; /* number of active regions */
+
+ /* Port I/O or MMIO Regions */
+ PCIRegion regions[MAX_IO_REGIONS];
+ int config_fd;
+} PCIDevRegions;
+
+typedef struct {
+ target_phys_addr_t e_physbase;
+ uint32_t memory_index;
+ union {
+ void *r_virtbase; /* mmapped access address for memory regions */
+ uint32_t r_baseport; /* the base guest port for I/O regions */
+ } u;
+ int num; /* our index within v_addrs[] */
+ uint32_t e_size; /* emulated size of region in bytes */
+ uint32_t r_size; /* real size of region in bytes */
+} AssignedDevRegion;
+
+typedef struct {
+ PCIDevice dev;
+ int intpin;
+ uint8_t debug_flags;
+ AssignedDevRegion v_addrs[PCI_NUM_REGIONS];
+ PCIDevRegions real_device;
+ int run;
+ int girq;
+ unsigned char h_busnr;
+ unsigned int h_devfn;
+ int bound;
+} AssignedDevice;
+
+typedef struct AssignedDevInfo AssignedDevInfo;
+
+struct AssignedDevInfo {
+ char name[15];
+ int bus;
+ int dev;
+ int func;
+ AssignedDevice *assigned_dev;
+ LIST_ENTRY(AssignedDevInfo) next;
+ int disable_iommu;
+};
+
+void free_assigned_device(AssignedDevInfo *adev);
+PCIDevice *init_assigned_device(AssignedDevInfo *adev, PCIBus *bus);
+AssignedDevInfo *add_assigned_device(const char *arg);
+void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices);
+
+#define MAX_DEV_ASSIGN_CMDLINE 8
+
+extern const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE];
+extern int assigned_devices_index;
+
+#endif /* __DEVICE_ASSIGNMENT_H__ */
diff --git a/hw/device-hotplug.c b/hw/device-hotplug.c
new file mode 100644
index 000000000..0bcac601b
--- /dev/null
+++ b/hw/device-hotplug.c
@@ -0,0 +1,264 @@
+#include "hw.h"
+#include "boards.h"
+#include "pci.h"
+#include "net.h"
+#include "sysemu.h"
+#include "pc.h"
+#include "console.h"
+#include "block_int.h"
+#include "device-assignment.h"
+#include "config.h"
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_BASE_CLASS_NETWORK 0x02
+
+static PCIDevice *qemu_system_hot_add_nic(const char *opts, int bus_nr)
+{
+ int ret;
+ PCIBus *pci_bus;
+
+ pci_bus = pci_find_bus (bus_nr);
+ if (!pci_bus) {
+ term_printf ("Can't find pci_bus %d\n", bus_nr);
+ return NULL;
+ }
+
+ ret = net_client_init ("nic", opts);
+ if (ret < 0 || !nd_table[ret].model)
+ return NULL;
+ return pci_nic_init (pci_bus, &nd_table[ret], -1);
+}
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+static PCIDevice *qemu_system_hot_assign_device(const char *opts, int bus_nr)
+{
+ PCIBus *pci_bus;
+ AssignedDevInfo *adev;
+ PCIDevice *ret;
+
+ pci_bus = pci_find_bus(bus_nr);
+ if (!pci_bus) {
+ term_printf ("Can't find pci_bus %d\n", bus_nr);
+ return NULL;
+ }
+ adev = add_assigned_device(opts);
+ if (adev == NULL) {
+ term_printf ("Error adding device; check syntax\n");
+ return NULL;
+ }
+
+ ret = init_assigned_device(adev, pci_bus);
+ if (ret == NULL) {
+ term_printf("Failed to assign device\n");
+ free_assigned_device(adev);
+ return NULL;
+ }
+
+ term_printf("Registered host PCI device %02x:%02x.%1x "
+ "(\"%s\") as guest device %02x:%02x.%1x\n",
+ adev->bus, adev->dev, adev->func, adev->name,
+ pci_bus_num(pci_bus), (ret->devfn >> 3) & 0x1f,
+ adev->func);
+
+ return ret;
+}
+
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+
+static int add_init_drive(const char *opts)
+{
+ int drive_opt_idx, drive_idx;
+ int ret = -1;
+
+ drive_opt_idx = drive_add(NULL, "%s", opts);
+ if (!drive_opt_idx)
+ return ret;
+
+ drive_idx = drive_init(&drives_opt[drive_opt_idx], 0, current_machine);
+ if (drive_idx == -1) {
+ drive_remove(drive_opt_idx);
+ return ret;
+ }
+
+ return drive_idx;
+}
+
+void drive_hot_add(int pcibus, const char *devfn_string, const char *opts)
+{
+ int drive_idx, type, bus;
+ int devfn;
+ int success = 0;
+ PCIDevice *dev;
+
+ devfn = strtoul(devfn_string, NULL, 0);
+
+ dev = pci_find_device(pcibus, PCI_SLOT(devfn));
+ if (!dev) {
+ term_printf("no pci device with devfn %d (slot %d)\n", devfn,
+ PCI_SLOT(devfn));
+ return;
+ }
+
+ drive_idx = add_init_drive(opts);
+ if (drive_idx < 0)
+ return;
+ type = drives_table[drive_idx].type;
+ bus = drive_get_max_bus (type);
+
+ switch (type) {
+ case IF_SCSI:
+ success = 1;
+ lsi_scsi_attach (dev, drives_table[drive_idx].bdrv,
+ drives_table[drive_idx].unit);
+ break;
+ default:
+ term_printf("Can't hot-add drive to type %d\n", type);
+ }
+
+ if (success)
+ term_printf("OK bus %d, unit %d\n", drives_table[drive_idx].bus,
+ drives_table[drive_idx].unit);
+ return;
+}
+
+static PCIDevice *qemu_system_hot_add_storage(const char *opts, int bus_nr)
+{
+ void *opaque = NULL;
+ PCIBus *pci_bus;
+ int type = -1, drive_idx = -1;
+ char buf[128];
+
+ pci_bus = pci_find_bus(bus_nr);
+ if (!pci_bus) {
+ term_printf("Can't find pci_bus %d\n", bus_nr);
+ return NULL;
+ }
+
+ if (get_param_value(buf, sizeof(buf), "if", opts)) {
+ if (!strcmp(buf, "scsi"))
+ type = IF_SCSI;
+ else if (!strcmp(buf, "virtio")) {
+ type = IF_VIRTIO;
+ }
+ } else {
+ term_printf("no if= specified\n");
+ return NULL;
+ }
+
+ if (get_param_value(buf, sizeof(buf), "file", opts)) {
+ drive_idx = add_init_drive(opts);
+ if (drive_idx < 0)
+ return NULL;
+ } else if (type == IF_VIRTIO) {
+ term_printf("virtio requires a backing file/device.\n");
+ return NULL;
+ }
+
+ switch (type) {
+ case IF_SCSI:
+ opaque = lsi_scsi_init (pci_bus, -1);
+ if (opaque && drive_idx >= 0)
+ lsi_scsi_attach (opaque, drives_table[drive_idx].bdrv,
+ drives_table[drive_idx].unit);
+ break;
+ case IF_VIRTIO:
+ opaque = virtio_blk_init (pci_bus, 0x1AF4, 0x1001,
+ drives_table[drive_idx].bdrv);
+ break;
+ default:
+ term_printf ("type %s not a hotpluggable PCI device.\n", buf);
+ }
+
+ return opaque;
+}
+
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+void device_hot_add(int pcibus, const char *type, const char *opts)
+{
+ PCIDevice *dev = NULL;
+
+ if (strcmp(type, "nic") == 0)
+ dev = qemu_system_hot_add_nic(opts, pcibus);
+ else if (strcmp(type, "storage") == 0)
+ dev = qemu_system_hot_add_storage(opts, pcibus);
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+ else if (strcmp(type, "host") == 0)
+ dev = qemu_system_hot_assign_device(opts, pcibus);
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+ else
+ term_printf("invalid type: %s\n", type);
+
+ if (dev) {
+ qemu_system_device_hot_add(pcibus, PCI_SLOT(dev->devfn), 1);
+ term_printf("OK bus %d, slot %d, function %d (devfn %d)\n",
+ pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), dev->devfn);
+ } else
+ term_printf("failed to add %s\n", opts);
+}
+
+void device_hot_remove(int pcibus, int slot)
+{
+ PCIDevice *d = pci_find_device(pcibus, slot);
+
+ if (!d) {
+ term_printf("invalid slot %d\n", slot);
+ return;
+ }
+
+ qemu_system_device_hot_add(pcibus, slot, 0);
+}
+#endif
+
+static void destroy_nic(int slot)
+{
+ int i;
+
+ for (i = 0; i < MAX_NICS; i++)
+ if (nd_table[i].used &&
+ PCI_SLOT(nd_table[i].devfn) == slot)
+ net_client_uninit(&nd_table[i]);
+}
+
+static void destroy_bdrvs(int slot)
+{
+ int i;
+ struct BlockDriverState *bs;
+
+ for (i = 0; i <= MAX_DRIVES; i++) {
+ bs = drives_table[i].bdrv;
+ if (bs && (PCI_SLOT(bs->devfn) == slot)) {
+ drive_uninit(bs);
+ bdrv_delete(bs);
+ }
+ }
+}
+
+/*
+ * OS has executed _EJ0 method, we now can remove the device
+ */
+void device_hot_remove_success(int pcibus, int slot)
+{
+ PCIDevice *d = pci_find_device(pcibus, slot);
+ int class_code;
+
+ if (!d) {
+ term_printf("invalid slot %d\n", slot);
+ return;
+ }
+
+ class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1);
+
+ pci_unregister_device(d);
+
+ switch(class_code) {
+ case PCI_BASE_CLASS_STORAGE:
+ destroy_bdrvs(slot);
+ break;
+ case PCI_BASE_CLASS_NETWORK:
+ destroy_nic(slot);
+ break;
+ }
+
+}
+
diff --git a/hw/ds1225y.c b/hw/ds1225y.c
index 64c21b722..9bc12ccbc 100644
--- a/hw/ds1225y.c
+++ b/hw/ds1225y.c
@@ -162,13 +162,13 @@ void *ds1225y_init(target_phys_addr_t mem_base, const char *filename)
s->protection = 7;
/* Read current file */
- file = qemu_fopen(filename, "rb");
+ file = qemu_fopen_file(filename, "rb");
if (file) {
/* Read nvram contents */
qemu_get_buffer(file, s->contents, s->chip_size);
qemu_fclose(file);
}
- s->file = qemu_fopen(filename, "wb");
+ s->file = qemu_fopen_file(filename, "wb");
if (s->file) {
/* Write back contents, as 'wb' mode cleaned the file */
qemu_put_buffer(s->file, s->contents, s->chip_size);
diff --git a/hw/e1000.c b/hw/e1000.c
index c326671b0..b0050b0d2 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -26,6 +26,7 @@
#include "hw.h"
#include "pci.h"
#include "net.h"
+#include "qemu-kvm.h"
#include "e1000_hw.h"
@@ -1019,7 +1020,17 @@ e1000_mmio_map(PCIDevice *pci_dev, int region_num,
excluded_regs[i] - 4);
}
-void
+static int
+pci_e1000_uninit(PCIDevice *dev)
+{
+ E1000State *d = (E1000State *) dev;
+
+ cpu_unregister_io_memory(d->mmio_index);
+
+ return 0;
+}
+
+PCIDevice *
pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
{
E1000State *d;
@@ -1031,6 +1042,9 @@ pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
d = (E1000State *)pci_register_device(bus, "e1000",
sizeof(E1000State), devfn, NULL, NULL);
+ if (!d)
+ return NULL;
+
pci_conf = d->dev.config;
memset(pci_conf, 0, 256);
@@ -1080,4 +1094,7 @@ pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
d->nd->macaddr[3], d->nd->macaddr[4], d->nd->macaddr[5]);
register_savevm(info_str, -1, 2, nic_save, nic_load, d);
+ d->dev.unregister = pci_e1000_uninit;
+
+ return (PCIDevice *)d;
}
diff --git a/hw/eepro100.c b/hw/eepro100.c
index cb3ca0964..ce7d9104b 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1736,7 +1736,7 @@ static void nic_save(QEMUFile * f, void *opaque)
qemu_put_buffer(f, s->configuration, sizeof(s->configuration));
}
-static void nic_init(PCIBus * bus, NICInfo * nd,
+static PCIDevice *nic_init(PCIBus * bus, NICInfo * nd,
const char *name, uint32_t device)
{
PCIEEPRO100State *d;
@@ -1747,6 +1747,8 @@ static void nic_init(PCIBus * bus, NICInfo * nd,
d = (PCIEEPRO100State *) pci_register_device(bus, name,
sizeof(PCIEEPRO100State), -1,
NULL, NULL);
+ if (!d)
+ return NULL;
s = &d->eepro100;
s->device = device;
@@ -1787,22 +1789,23 @@ static void nic_init(PCIBus * bus, NICInfo * nd,
qemu_register_reset(nic_reset, s);
register_savevm(name, -1, 3, nic_save, nic_load, s);
+ return (PCIDevice *)d;
}
-void pci_i82551_init(PCIBus * bus, NICInfo * nd, int devfn)
+PCIDevice *pci_i82551_init(PCIBus * bus, NICInfo * nd, int devfn)
{
- nic_init(bus, nd, "i82551", i82551);
+ return nic_init(bus, nd, "i82551", i82551);
//~ uint8_t *pci_conf = d->dev.config;
}
-void pci_i82557b_init(PCIBus * bus, NICInfo * nd, int devfn)
+PCIDevice *pci_i82557b_init(PCIBus * bus, NICInfo * nd, int devfn)
{
- nic_init(bus, nd, "i82557b", i82557B);
+ return nic_init(bus, nd, "i82557b", i82557B);
}
-void pci_i82559er_init(PCIBus * bus, NICInfo * nd, int devfn)
+PCIDevice *pci_i82559er_init(PCIBus * bus, NICInfo * nd, int devfn)
{
- nic_init(bus, nd, "i82559er", i82559ER);
+ return nic_init(bus, nd, "i82559er", i82559ER);
}
/* eof */
diff --git a/hw/extboot.c b/hw/extboot.c
new file mode 100644
index 000000000..056fb59fe
--- /dev/null
+++ b/hw/extboot.c
@@ -0,0 +1,132 @@
+/*
+ * Extended boot option ROM support.
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "block.h"
+
+/* Extended Boot ROM suport */
+
+union extboot_cmd
+{
+ uint16_t type;
+ struct {
+ uint16_t type;
+ uint16_t cylinders;
+ uint16_t heads;
+ uint16_t sectors;
+ uint64_t nb_sectors;
+ } query_geometry;
+ struct {
+ uint16_t type;
+ uint16_t nb_sectors;
+ uint16_t segment;
+ uint16_t offset;
+ uint64_t sector;
+ } xfer;
+};
+
+static void get_translated_chs(BlockDriverState *bs, int *c, int *h, int *s)
+{
+ bdrv_get_geometry_hint(bs, c, h, s);
+
+ if (*c <= 1024) {
+ *c >>= 0;
+ *h <<= 0;
+ } else if (*c <= 2048) {
+ *c >>= 1;
+ *h <<= 1;
+ } else if (*c <= 4096) {
+ *c >>= 2;
+ *h <<= 2;
+ } else if (*c <= 8192) {
+ *c >>= 3;
+ *h <<= 3;
+ } else {
+ *c >>= 4;
+ *h <<= 4;
+ }
+
+ /* what is the correct algorithm for this?? */
+ if (*h == 256) {
+ *h = 255;
+ *c = *c + 1;
+ }
+}
+
+static uint32_t extboot_read(void *opaque, uint32_t addr)
+{
+ int *pcmd = opaque;
+ return *pcmd;
+}
+
+static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value)
+{
+ union extboot_cmd *cmd = (void *)(phys_ram_base + ((value & 0xFFFF) << 4));
+ BlockDriverState *bs = opaque;
+ int cylinders, heads, sectors, err;
+ int64_t nb_sectors;
+
+ get_translated_chs(bs, &cylinders, &heads, &sectors);
+
+ if (cmd->type == 0x01 || cmd->type == 0x02) {
+ target_ulong pa = cmd->xfer.segment * 16 + cmd->xfer.segment;
+
+ /* possible buffer overflow */
+ if ((pa + cmd->xfer.nb_sectors * 512) > phys_ram_size)
+ return;
+ }
+
+ switch (cmd->type) {
+ case 0x00:
+ bdrv_get_geometry(bs, &nb_sectors);
+ cmd->query_geometry.cylinders = cylinders;
+ cmd->query_geometry.heads = heads;
+ cmd->query_geometry.sectors = sectors;
+ cmd->query_geometry.nb_sectors = nb_sectors;
+ cpu_physical_memory_set_dirty((value & 0xFFFF) << 4);
+ break;
+ case 0x01:
+ err = bdrv_read(bs, cmd->xfer.sector, phys_ram_base +
+ cmd->xfer.segment * 16 + cmd->xfer.offset,
+ cmd->xfer.nb_sectors);
+ if (err)
+ printf("Read failed\n");
+ break;
+ case 0x02:
+ err = bdrv_write(bs, cmd->xfer.sector, phys_ram_base +
+ cmd->xfer.segment * 16 + cmd->xfer.offset,
+ cmd->xfer.nb_sectors);
+ if (err)
+ printf("Write failed\n");
+
+ cpu_physical_memory_set_dirty(cmd->xfer.segment * 16 + cmd->xfer.offset);
+ break;
+ }
+}
+
+void extboot_init(BlockDriverState *bs, int cmd)
+{
+ int *pcmd;
+
+ pcmd = qemu_mallocz(sizeof(int));
+ if (!pcmd) {
+ fprintf(stderr, "Error allocating memory\n");
+ exit(1);
+ }
+
+ *pcmd = cmd;
+ register_ioport_read(0x404, 1, 1, extboot_read, pcmd);
+ register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs);
+}
diff --git a/hw/i8254-kvm.c b/hw/i8254-kvm.c
new file mode 100644
index 000000000..127b34736
--- /dev/null
+++ b/hw/i8254-kvm.c
@@ -0,0 +1,108 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qemu-timer.h"
+#include "i8254.h"
+#include "qemu-kvm.h"
+
+static PITState pit_state;
+
+static void kvm_pit_save(QEMUFile *f, void *opaque)
+{
+ PITState *s = opaque;
+ struct kvm_pit_state pit;
+ struct kvm_pit_channel_state *c;
+ struct PITChannelState *sc;
+ int i;
+
+ kvm_get_pit(kvm_context, &pit);
+
+ for (i = 0; i < 3; i++) {
+ c = &pit.channels[i];
+ sc = &s->channels[i];
+ sc->count = c->count;
+ sc->latched_count = c->latched_count;
+ sc->count_latched = c->count_latched;
+ sc->status_latched = c->status_latched;
+ sc->status = c->status;
+ sc->read_state = c->read_state;
+ sc->write_state = c->write_state;
+ sc->write_latch = c->write_latch;
+ sc->rw_mode = c->rw_mode;
+ sc->mode = c->mode;
+ sc->bcd = c->bcd;
+ sc->gate = c->gate;
+ sc->count_load_time = c->count_load_time;
+ }
+
+ pit_save(f, s);
+}
+
+static int kvm_pit_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PITState *s = opaque;
+ struct kvm_pit_state pit;
+ struct kvm_pit_channel_state *c;
+ struct PITChannelState *sc;
+ int i;
+
+ pit_load(f, s, version_id);
+
+ for (i = 0; i < 3; i++) {
+ c = &pit.channels[i];
+ sc = &s->channels[i];
+ c->count = sc->count;
+ c->latched_count = sc->latched_count;
+ c->count_latched = sc->count_latched;
+ c->status_latched = sc->status_latched;
+ c->status = sc->status;
+ c->read_state = sc->read_state;
+ c->write_state = sc->write_state;
+ c->write_latch = sc->write_latch;
+ c->rw_mode = sc->rw_mode;
+ c->mode = sc->mode;
+ c->bcd = sc->bcd;
+ c->gate = sc->gate;
+ c->count_load_time = sc->count_load_time;
+ }
+
+ kvm_set_pit(kvm_context, &pit);
+
+ return 0;
+}
+
+PITState *kvm_pit_init(int base, qemu_irq irq)
+{
+ PITState *pit = &pit_state;
+
+ register_savevm(PIT_SAVEVM_NAME, base, PIT_SAVEVM_VERSION,
+ kvm_pit_save, kvm_pit_load, pit);
+
+ qemu_register_reset(pit_reset, pit);
+ pit_reset(pit);
+
+ return pit;
+}
diff --git a/hw/i8254.c b/hw/i8254.c
index a4a1efe9c..e6be0cd3c 100644
--- a/hw/i8254.c
+++ b/hw/i8254.c
@@ -25,38 +25,10 @@
#include "pc.h"
#include "isa.h"
#include "qemu-timer.h"
+#include "i8254.h"
//#define DEBUG_PIT
-#define RW_STATE_LSB 1
-#define RW_STATE_MSB 2
-#define RW_STATE_WORD0 3
-#define RW_STATE_WORD1 4
-
-typedef struct PITChannelState {
- int count; /* can be 65536 */
- uint16_t latched_count;
- uint8_t count_latched;
- uint8_t status_latched;
- uint8_t status;
- uint8_t read_state;
- uint8_t write_state;
- uint8_t write_latch;
- uint8_t rw_mode;
- uint8_t mode;
- uint8_t bcd; /* not supported */
- uint8_t gate; /* timer start */
- int64_t count_load_time;
- /* irq handling */
- int64_t next_transition_time;
- QEMUTimer *irq_timer;
- qemu_irq irq;
-} PITChannelState;
-
-struct PITState {
- PITChannelState channels[3];
-};
-
static PITState pit_state;
static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
@@ -360,6 +332,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 +347,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,
@@ -389,7 +384,7 @@ static void pit_irq_timer(void *opaque)
pit_irq_timer_update(s, s->next_transition_time);
}
-static void pit_save(QEMUFile *f, void *opaque)
+void pit_save(QEMUFile *f, void *opaque)
{
PITState *pit = opaque;
PITChannelState *s;
@@ -417,7 +412,7 @@ static void pit_save(QEMUFile *f, void *opaque)
}
}
-static int pit_load(QEMUFile *f, void *opaque, int version_id)
+int pit_load(QEMUFile *f, void *opaque, int version_id)
{
PITState *pit = opaque;
PITChannelState *s;
@@ -446,10 +441,11 @@ static int pit_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_timer(f, s->irq_timer);
}
}
+
return 0;
}
-static void pit_reset(void *opaque)
+void pit_reset(void *opaque)
{
PITState *pit = opaque;
PITChannelState *s;
@@ -494,7 +490,8 @@ PITState *pit_init(int base, qemu_irq irq)
s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
s->irq = irq;
- register_savevm("i8254", base, 1, pit_save, pit_load, pit);
+ register_savevm(PIT_SAVEVM_NAME, base, PIT_SAVEVM_VERSION,
+ pit_save, pit_load, pit);
qemu_register_reset(pit_reset, pit);
register_ioport_write(base, 4, 1, pit_ioport_write, pit);
diff --git a/hw/i8254.h b/hw/i8254.h
new file mode 100644
index 000000000..ee68ab55b
--- /dev/null
+++ b/hw/i8254.h
@@ -0,0 +1,66 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_I8254_H
+#define QEMU_I8254_H
+
+#define PIT_SAVEVM_NAME "i8254"
+#define PIT_SAVEVM_VERSION 1
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+typedef struct PITChannelState {
+ int count; /* can be 65536 */
+ uint16_t latched_count;
+ uint8_t count_latched;
+ uint8_t status_latched;
+ uint8_t status;
+ uint8_t read_state;
+ uint8_t write_state;
+ uint8_t write_latch;
+ uint8_t rw_mode;
+ uint8_t mode;
+ uint8_t bcd; /* not supported */
+ uint8_t gate; /* timer start */
+ int64_t count_load_time;
+ /* irq handling */
+ int64_t next_transition_time;
+ QEMUTimer *irq_timer;
+ qemu_irq irq;
+} PITChannelState;
+
+struct PITState {
+ PITChannelState channels[3];
+};
+
+void pit_save(QEMUFile *f, void *opaque);
+
+int pit_load(QEMUFile *f, void *opaque, int version_id);
+
+void pit_reset(void *opaque);
+
+#endif
diff --git a/hw/i8259.c b/hw/i8259.c
index 750a76ca9..6d41a5e51 100644
--- a/hw/i8259.c
+++ b/hw/i8259.c
@@ -26,6 +26,8 @@
#include "isa.h"
#include "console.h"
+#include "qemu-kvm.h"
+
/* debug PIC */
//#define DEBUG_PIC
@@ -183,7 +185,11 @@ int64_t irq_time[16];
static void i8259_set_irq(void *opaque, int irq, int level)
{
PicState2 *s = opaque;
-
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled())
+ if (kvm_set_irq(irq, level))
+ return;
+#endif
#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
if (level != irq_level[irq]) {
#if defined(DEBUG_PIC)
@@ -217,18 +223,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) {
@@ -450,10 +473,77 @@ static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
return s->elcr;
}
+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
+}
+
static void pic_save(QEMUFile *f, void *opaque)
{
PicState *s = opaque;
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_pic_save_to_user(s);
+ }
+
qemu_put_8s(f, &s->last_irr);
qemu_put_8s(f, &s->irr);
qemu_put_8s(f, &s->imr);
@@ -495,6 +585,11 @@ 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);
+
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_kernel_pic_load_from_user(s);
+ }
+
return 0;
}
diff --git a/hw/ide.c b/hw/ide.c
index 69ab773e6..7a7dedcb4 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -28,6 +28,7 @@
#include "scsi-disk.h"
#include "pcmcia.h"
#include "block.h"
+#include "block_int.h"
#include "qemu-timer.h"
#include "sysemu.h"
#include "ppc_mac.h"
@@ -3225,6 +3226,7 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
{
PCIIDEState *d;
uint8_t *pci_conf;
+ int i;
/* register a function 1 of PIIX3 */
d = (PCIIDEState *)pci_register_device(bus, "PIIX3 IDE",
@@ -3254,6 +3256,10 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6);
ide_init_ioport(&d->ide_if[2], 0x170, 0x376);
+ for (i = 0; i < 4; i++)
+ if (hd_table[i])
+ hd_table[i]->devfn = d->dev.devfn;
+
register_savevm("ide", 0, 1, pci_ide_save, pci_ide_load, d);
}
diff --git a/hw/ipf.c b/hw/ipf.c
new file mode 100644
index 000000000..3e24c9853
--- /dev/null
+++ b/hw/ipf.c
@@ -0,0 +1,708 @@
+/*
+ * Itanium Platform Emulator derived from QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Copyright (c) 2007 Intel
+ * Ported for IA64 Platform Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "pci.h"
+#include "block.h"
+#include "sysemu.h"
+#include "audio/audio.h"
+#include "net.h"
+#include "smbus.h"
+#include "boards.h"
+#include "firmware.h"
+#include "ia64intrin.h"
+#include <unistd.h>
+#include "device-assignment.h"
+
+#include "qemu-kvm.h"
+
+#define FW_FILENAME "Flash.fd"
+
+/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */
+#define ACPI_DATA_SIZE 0x10000
+
+#define MAX_IDE_BUS 2
+
+static fdctrl_t *floppy_controller;
+static RTCState *rtc_state;
+static PCIDevice *i440fx_state;
+
+static uint32_t ipf_to_legacy_io(target_phys_addr_t addr)
+{
+ return (uint32_t)(((addr&0x3ffffff) >> 12 << 2)|((addr) & 0x3));
+}
+
+static void ipf_legacy_io_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val) {
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ cpu_outb(0, port, val);
+}
+
+static void ipf_legacy_io_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val) {
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ cpu_outw(0, port, val);
+}
+
+static void ipf_legacy_io_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val) {
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ cpu_outl(0, port, val);
+}
+
+static uint32_t ipf_legacy_io_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ return cpu_inb(0, port);
+}
+
+static uint32_t ipf_legacy_io_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ return cpu_inw(0, port);
+}
+
+static uint32_t ipf_legacy_io_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ return cpu_inl(0, port);
+}
+
+static CPUReadMemoryFunc *ipf_legacy_io_read[3] = {
+ ipf_legacy_io_readb,
+ ipf_legacy_io_readw,
+ ipf_legacy_io_readl,
+};
+
+static CPUWriteMemoryFunc *ipf_legacy_io_write[3] = {
+ ipf_legacy_io_writeb,
+ ipf_legacy_io_writew,
+ ipf_legacy_io_writel,
+};
+
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+ fprintf(stderr,"pic_irq_request called!\n");
+}
+
+/* PC cmos mappings */
+
+#define REG_EQUIPMENT_BYTE 0x14
+
+static int cmos_get_fd_drive_type(int fd0)
+{
+ int val;
+
+ switch (fd0) {
+ case 0:
+ /* 1.44 Mb 3"5 drive */
+ val = 4;
+ break;
+ case 1:
+ /* 2.88 Mb 3"5 drive */
+ val = 5;
+ break;
+ case 2:
+ /* 1.2 Mb 5"5 drive */
+ val = 2;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+{
+ RTCState *s = rtc_state;
+ int cylinders, heads, sectors;
+
+ bdrv_get_geometry_hint(hd, &cylinders, &heads, &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);
+}
+
+/* convert boot_device letter to something recognizable by the bios */
+static int boot_device2nibble(char boot_device)
+{
+ switch(boot_device) {
+ case 'a':
+ case 'b':
+ return 0x01; /* floppy boot */
+ case 'c':
+ return 0x02; /* hard drive boot */
+ case 'd':
+ return 0x03; /* CD-ROM boot */
+ case 'n':
+ return 0x04; /* Network boot */
+ }
+ return 0;
+}
+
+/* hd_table must contain 4 block drivers */
+static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device, BlockDriverState **hd_table)
+{
+ RTCState *s = rtc_state;
+ int nbds, bds[3] = { 0, };
+ int val;
+ int fd0, fd1, nb;
+ int i;
+
+ /* various important CMOS locations needed by PC/Bochs bios */
+
+ /* memory size */
+ val = 640; /* base memory in K */
+ rtc_set_memory(s, 0x15, val);
+ rtc_set_memory(s, 0x16, val >> 8);
+
+ val = (ram_size / 1024) - 1024;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x17, val);
+ rtc_set_memory(s, 0x18, val >> 8);
+ rtc_set_memory(s, 0x30, val);
+ rtc_set_memory(s, 0x31, val >> 8);
+
+ if (above_4g_mem_size) {
+ rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16);
+ rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
+ rtc_set_memory(s, 0x5d, above_4g_mem_size >> 32);
+ }
+ rtc_set_memory(s, 0x5f, smp_cpus - 1);
+
+ if (ram_size > (16 * 1024 * 1024))
+ val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
+ else
+ val = 0;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x34, val);
+ rtc_set_memory(s, 0x35, val >> 8);
+
+ /* set boot devices, and disable floppy signature check if requested */
+#define PC_MAX_BOOT_DEVICES 3
+ nbds = strlen(boot_device);
+
+ if (nbds > PC_MAX_BOOT_DEVICES) {
+ fprintf(stderr, "Too many boot devices for PC\n");
+ exit(1);
+ }
+
+ for (i = 0; i < nbds; i++) {
+ bds[i] = boot_device2nibble(boot_device[i]);
+ if (bds[i] == 0) {
+ fprintf(stderr, "Invalid boot device for PC: '%c'\n",
+ boot_device[i]);
+ exit(1);
+ }
+ }
+
+ rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+ rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1));
+
+ /* floppy type */
+
+ fd0 = fdctrl_get_drive_type(floppy_controller, 0);
+ fd1 = fdctrl_get_drive_type(floppy_controller, 1);
+
+ val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
+ rtc_set_memory(s, 0x10, val);
+
+ val = 0;
+ nb = 0;
+ if (fd0 < 3)
+ nb++;
+ if (fd1 < 3)
+ nb++;
+
+ switch (nb) {
+ case 0:
+ break;
+ case 1:
+ val |= 0x01; /* 1 drive, ready for boot */
+ break;
+ case 2:
+ val |= 0x41; /* 2 drives, ready for boot */
+ break;
+ }
+
+ val |= 0x02; /* FPU is there */
+ val |= 0x04; /* PS/2 mouse installed */
+ rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
+
+ /* hard drives */
+
+ rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
+ if (hd_table[0])
+ cmos_init_hd(0x19, 0x1b, hd_table[0]);
+ if (hd_table[1])
+ cmos_init_hd(0x1a, 0x24, hd_table[1]);
+
+ val = 0;
+ for (i = 0; i < 4; i++) {
+ if (hd_table[i]) {
+ int cylinders, heads, sectors, translation;
+ /* NOTE: bdrv_get_geometry_hint() returns the physical
+ geometry. It is always such that: 1 <= sects <= 63, 1
+ <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+ geometry can be different if a translation is done. */
+ translation = bdrv_get_translation_hint(hd_table[i]);
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_get_geometry_hint(hd_table[i], &cylinders,
+ &heads, &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);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+#define NE2000_NB_MAX 6
+
+static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340,
+ 0x360, 0x280, 0x380 };
+static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+
+#ifdef HAS_AUDIO
+static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
+{
+ struct soundhw *c;
+ int audio_enabled = 0;
+
+ for (c = soundhw; !audio_enabled && c->name; ++c) {
+ audio_enabled = c->enabled;
+ }
+
+ if (audio_enabled) {
+ AudioState *s;
+
+ s = AUD_init ();
+ if (s) {
+ for (c = soundhw; c->name; ++c) {
+ if (c->enabled) {
+ if (c->isa) {
+ c->init.init_isa (s, pic);
+ } else {
+ if (pci_bus) {
+ c->init.init_pci (pci_bus, s);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic)
+{
+ static int nb_ne2k = 0;
+
+ if (nb_ne2k == NE2000_NB_MAX)
+ return;
+ isa_ne2000_init(ne2000_io[nb_ne2k], pic[ne2000_irq[nb_ne2k]], nd);
+ nb_ne2k++;
+}
+
+/* Itanium hardware initialisation */
+static void ipf_init1(ram_addr_t ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename,
+ int pci_enabled, const char *cpu_model)
+{
+ char buf[1024];
+ int i;
+ ram_addr_t ram_addr, 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;
+ int index;
+ unsigned long ipf_legacy_io_base, ipf_legacy_io_mem;
+ BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ BlockDriverState *fd[MAX_FD];
+
+ page_size = getpagesize();
+ if (page_size != TARGET_PAGE_SIZE) {
+ fprintf(stderr,"Error! Host page size != qemu target page size,"
+ " you may need to change TARGET_PAGE_BITS in qemu!"
+ "host page size:0x%x\n", page_size);
+ exit(-1);
+ };
+
+ if (ram_size >= 0xc0000000 ) {
+ above_4g_mem_size = ram_size - 0xc0000000;
+ ram_size = 0xc0000000;
+ }
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "IA64";
+ }
+
+ for(i = 0; i < smp_cpus; i++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ if (i != 0)
+ env->hflags |= HF_HALTED_MASK;
+ register_savevm("cpu", i, 4, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+ }
+
+ /* allocate RAM */
+ if (kvm_enabled()) {
+ ram_addr = qemu_ram_alloc(0xa0000);
+ cpu_register_physical_memory(0, 0xa0000, ram_addr);
+
+ ram_addr = qemu_ram_alloc(0x20000); // Workaround 0xa0000-0xc0000
+
+ ram_addr = qemu_ram_alloc(0x40000);
+ cpu_register_physical_memory(0xc0000, 0x40000, ram_addr);
+
+ ram_addr = qemu_ram_alloc(ram_size - 0x100000);
+ cpu_register_physical_memory(0x100000, ram_size - 0x100000, ram_addr);
+ } else {
+ ram_addr = qemu_ram_alloc(ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_addr);
+ }
+ /* 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);
+ }
+
+ /*Load firware to its proper position.*/
+ if (kvm_enabled()) {
+ unsigned long image_size;
+ char *image = NULL;
+ uint8_t *fw_image_start;
+ ram_addr_t fw_offset = qemu_ram_alloc(GFW_SIZE);
+ uint8_t *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");
+ fprintf(stderr, "Please check Guest firmware at %s\n", buf);
+ exit(1);
+ }
+ fw_image_start = fw_start + GFW_SIZE - image_size;
+
+ cpu_register_physical_memory(GFW_START, GFW_SIZE, fw_offset);
+ memcpy(fw_image_start, image, image_size);
+
+ free(image);
+ flush_icache_range((unsigned long)fw_image_start,
+ (unsigned long)fw_image_start + image_size);
+ kvm_ia64_build_hob(ram_size + above_4g_mem_size, smp_cpus, fw_start);
+ }
+
+ /*Register legacy io address space, size:64M*/
+ ipf_legacy_io_base = 0xE0000000;
+ ipf_legacy_io_mem = cpu_register_io_memory(0, ipf_legacy_io_read,
+ ipf_legacy_io_write, NULL);
+ cpu_register_physical_memory(ipf_legacy_io_base, 64*1024*1024,
+ ipf_legacy_io_mem);
+
+ cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1);
+ i8259 = 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]], 115200,
+ serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(parallel_io[i], i8259[parallel_irq[i]],
+ parallel_hds[i]);
+ }
+ }
+
+ for(i = 0; i < nb_nics; i++) {
+ 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");
+ if (!pci_nic_init(pci_bus, nd, -1))
+ exit(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 it now, need to implement later!
+#ifdef USE_HYPERCALL
+ pci_hypercall_init(pci_bus);
+#endif
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ index = drive_get_index(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ if (index != -1)
+ hd[i] = drives_table[index].bdrv;
+ else
+ hd[i] = NULL;
+ }
+
+ if (pci_enabled) {
+ pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1, i8259);
+ } else {
+ for(i = 0; i < MAX_IDE_BUS; i++) {
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], i8259[ide_irq[i]],
+ hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
+ }
+ }
+
+ i8042_init(i8259[1], i8259[12], 0x60);
+ DMA_init(0);
+#ifdef HAS_AUDIO
+ audio_init(pci_enabled ? pci_bus : NULL, i8259);
+#endif
+
+ for(i = 0; i < MAX_FD; i++) {
+ index = drive_get_index(IF_FLOPPY, 0, i);
+ if (index != -1)
+ fd[i] = drives_table[index].bdrv;
+ else
+ fd[i] = NULL;
+ }
+ floppy_controller = fdctrl_init(i8259[6], 2, 0, 0x3f0, fd);
+
+ cmos_init(ram_size, above_4g_mem_size, boot_device, hd);
+
+ if (pci_enabled && usb_enabled) {
+ usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+ }
+
+ if (pci_enabled && acpi_enabled) {
+ uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+ i2c_bus *smbus;
+
+ /* TODO: Populate SPD eeprom data. */
+ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, i8259[9]);
+ for (i = 0; i < 8; i++) {
+ smbus_eeprom_device_init(smbus, 0x50 + i, eeprom_buf + (i * 256));
+ }
+ }
+
+ if (i440fx_state) {
+ i440fx_init_memory_mappings(i440fx_state);
+ }
+
+ if (pci_enabled) {
+ int max_bus;
+ int bus, unit;
+ void *scsi;
+
+ max_bus = drive_get_max_bus(IF_SCSI);
+
+ for (bus = 0; bus <= max_bus; bus++) {
+ scsi = lsi_scsi_init(pci_bus, -1);
+ for (unit = 0; unit < LSI_MAX_DEVS; unit++) {
+ index = drive_get_index(IF_SCSI, bus, unit);
+ if (index == -1)
+ continue;
+ lsi_scsi_attach(scsi, drives_table[index].bdrv, unit);
+ }
+ }
+ }
+ /* Add virtio block devices */
+ if (pci_enabled) {
+ int index;
+ int unit_id = 0;
+
+ while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) {
+ virtio_blk_init(pci_bus, 0x1AF4, 0x1001, drives_table[index].bdrv);
+ unit_id++;
+ }
+ }
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+ if (kvm_enabled())
+ add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+
+}
+
+static void ipf_init_pci(ram_addr_t ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ ipf_init1(ram_size, vga_ram_size, boot_device, ds, kernel_filename,
+ kernel_cmdline, initrd_filename, 1, cpu_model);
+}
+
+QEMUMachine ipf_machine = {
+ .name = "itanium",
+ .desc = "Itanium Platform",
+ .init = ipf_init_pci,
+ .ram_require = VGA_RAM_SIZE + GFW_SIZE,
+ .max_cpus = 255,
+};
+
+#define IOAPIC_NUM_PINS 48
+
+static int ioapic_irq_count[IOAPIC_NUM_PINS];
+
+static int ioapic_map_irq(int devfn, int irq_num)
+{
+ int irq, dev;
+ dev = devfn >> 3;
+ irq = ((((dev << 2) + (dev >> 3) + irq_num) & 31) + 16);
+ return irq;
+}
+
+void ioapic_set_irq(void *opaque, int irq_num, int level)
+{
+ int vector;
+
+ PCIDevice *pci_dev = (PCIDevice *)opaque;
+ vector = ioapic_map_irq(pci_dev->devfn, irq_num);
+
+ if (level)
+ ioapic_irq_count[vector] += 1;
+ else
+ ioapic_irq_count[vector] -= 1;
+
+ if (kvm_enabled()) {
+ if (kvm_set_irq(vector, ioapic_irq_count[vector] == 0))
+ return;
+ }
+}
+
+int ipf_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return ioapic_map_irq(pci_dev->devfn, irq_num);
+}
diff --git a/hw/irq.c b/hw/irq.c
index eca707dd0..7703f62c6 100644
--- a/hw/irq.c
+++ b/hw/irq.c
@@ -56,6 +56,12 @@ qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
return s;
}
+void qemu_free_irqs(qemu_irq *s)
+{
+ qemu_free(s[0]);
+ qemu_free(s);
+}
+
static void qemu_notirq(void *opaque, int line, int level)
{
struct IRQState *irq = opaque;
diff --git a/hw/irq.h b/hw/irq.h
index 0880ad21b..071539983 100644
--- a/hw/irq.h
+++ b/hw/irq.h
@@ -28,6 +28,8 @@ static inline void qemu_irq_pulse(qemu_irq irq)
/* Returns an array of N IRQs. */
qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n);
+void qemu_free_irqs(qemu_irq *s);
+
/* Returns a new IRQ with opposite polarity. */
qemu_irq qemu_irq_invert(qemu_irq irq);
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 81d567245..c65ff904e 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -13,6 +13,7 @@
#include "hw.h"
#include "pci.h"
#include "scsi-disk.h"
+#include "block_int.h"
//#define DEBUG_LSI
//#define DEBUG_LSI_REG
@@ -1957,6 +1958,19 @@ void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id)
s->scsi_dev[id] = scsi_generic_init(bd, 1, lsi_command_complete, s);
if (s->scsi_dev[id] == NULL)
s->scsi_dev[id] = scsi_disk_init(bd, 1, lsi_command_complete, s);
+ bd->devfn = s->pci_dev.devfn;
+}
+
+int lsi_scsi_uninit(PCIDevice *d)
+{
+ LSIState *s = (LSIState *) d;
+
+ cpu_unregister_io_memory(s->mmio_io_addr);
+ cpu_unregister_io_memory(s->ram_io_addr);
+
+ qemu_free(s->queue);
+
+ return 0;
}
void *lsi_scsi_init(PCIBus *bus, int devfn)
@@ -2000,6 +2014,7 @@ void *lsi_scsi_init(PCIBus *bus, int devfn)
s->queue = qemu_malloc(sizeof(lsi_queue));
s->queue_len = 1;
s->active_commands = 0;
+ s->pci_dev.unregister = lsi_scsi_uninit;
lsi_soft_reset(s);
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index 972c71c9d..dea9b589f 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -496,9 +496,11 @@ static void network_init (PCIBus *pci_bus)
}
if (i == 0 && strcmp(nd->model, "pcnet") == 0) {
/* The malta board has a PCNet card using PCI SLOT 11 */
- pci_nic_init(pci_bus, nd, 88);
+ if (!pci_nic_init(pci_bus, nd, 88))
+ exit(1);
} else {
- pci_nic_init(pci_bus, nd, -1);
+ if (!pci_nic_init(pci_bus, nd, -1))
+ exit(1);
}
}
}
diff --git a/hw/ne2000.c b/hw/ne2000.c
index 3f0ccf585..4efe7f3c1 100644
--- a/hw/ne2000.c
+++ b/hw/ne2000.c
@@ -784,7 +784,7 @@ static void ne2000_map(PCIDevice *pci_dev, int region_num,
register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
}
-void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn)
+PCIDevice *pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn)
{
PCINE2000State *d;
NE2000State *s;
@@ -794,6 +794,9 @@ void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn)
"NE2000", sizeof(PCINE2000State),
devfn,
NULL, NULL);
+ if (!d)
+ return NULL;
+
pci_conf = d->dev.config;
pci_conf[0x00] = 0xec; // Realtek 8029
pci_conf[0x01] = 0x10;
@@ -824,4 +827,6 @@ void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn)
s->macaddr[5]);
register_savevm("ne2000", -1, 3, ne2000_save, ne2000_load, s);
+
+ return (PCIDevice *)d;
}
diff --git a/hw/pc.c b/hw/pc.c
index 64c08a434..2d7e7f00e 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -36,6 +36,9 @@
#include "virtio-blk.h"
#include "virtio-balloon.h"
#include "hpet_emul.h"
+#include "device-assignment.h"
+
+#include "qemu-kvm.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@@ -43,6 +46,7 @@
#define BIOS_FILENAME "bios.bin"
#define VGABIOS_FILENAME "vgabios.bin"
#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+#define EXTBOOT_FILENAME "extboot.bin"
#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
@@ -256,6 +260,7 @@ static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
rtc_set_memory(s, 0x5d, (uint64_t)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);
@@ -749,6 +754,50 @@ static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic)
nb_ne2k++;
}
+typedef struct rom_reset_data {
+ uint8_t *data;
+ target_phys_addr_t addr;
+ unsigned size;
+} RomResetData;
+
+static void option_rom_reset(void *_rrd)
+{
+ RomResetData *rrd = _rrd;
+
+ cpu_physical_memory_write_rom(rrd->addr, rrd->data, rrd->size);
+}
+
+static void option_rom_setup_reset(target_phys_addr_t addr, unsigned size)
+{
+ RomResetData *rrd = qemu_malloc(sizeof *rrd);
+
+ rrd->data = qemu_malloc(size);
+ cpu_physical_memory_read(addr, rrd->data, size);
+ rrd->addr = addr;
+ rrd->size = size;
+ qemu_register_reset(option_rom_reset, rrd);
+}
+
+CPUState *pc_new_cpu(int cpu, const char *cpu_model, int pci_enabled)
+{
+ CPUState *env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find x86 CPU definition\n");
+ exit(1);
+ }
+ if (cpu != 0)
+ env->halted = 1;
+ if (smp_cpus > 1) {
+ /* XXX: enable it in all cases */
+ env->cpuid_features |= CPUID_APIC;
+ }
+ qemu_register_reset(main_cpu_reset, env);
+ if (pci_enabled) {
+ apic_init(env);
+ }
+ return env;
+}
+
/* PC hardware initialisation */
static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
@@ -760,7 +809,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
int ret, linux_boot, i;
ram_addr_t ram_addr, vga_ram_addr, bios_offset, vga_bios_offset;
ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
- int bios_size, isa_bios_size, vga_bios_size;
+ int bios_size, isa_bios_size, vga_bios_size, opt_rom_offset;
PCIBus *pci_bus;
int piix3_devfn = -1;
CPUState *env;
@@ -790,21 +839,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
}
for(i = 0; i < smp_cpus; i++) {
- env = cpu_init(cpu_model);
- if (!env) {
- fprintf(stderr, "Unable to find x86 CPU definition\n");
- exit(1);
- }
- if (i != 0)
- env->halted = 1;
- if (smp_cpus > 1) {
- /* XXX: enable it in all cases */
- env->cpuid_features |= CPUID_APIC;
- }
- qemu_register_reset(main_cpu_reset, env);
- if (pci_enabled) {
- apic_init(env);
- }
+ env = pc_new_cpu(i, cpu_model, pci_enabled);
}
vmport_init();
@@ -825,13 +860,20 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
/* above 4giga memory allocation */
if (above_4g_mem_size > 0) {
+ if (hpagesize) {
+ if (ram_addr & (hpagesize-1)) {
+ unsigned long aligned_addr;
+ aligned_addr = (ram_addr + hpagesize - 1) & ~(hpagesize-1);
+ qemu_ram_alloc(aligned_addr - ram_addr);
+ ram_addr = aligned_addr;
+ }
+ }
ram_addr = qemu_ram_alloc(above_4g_mem_size);
cpu_register_physical_memory(0x100000000ULL,
above_4g_mem_size,
ram_addr);
}
-
/* allocate VGA RAM */
vga_ram_addr = qemu_ram_alloc(vga_ram_size);
@@ -878,9 +920,17 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
isa_bios_size = bios_size;
if (isa_bios_size > (128 * 1024))
isa_bios_size = 128 * 1024;
+ cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size,
+ IO_MEM_UNASSIGNED);
+ /* kvm tpr optimization needs the bios accessible for write, at least to qemu itself */
cpu_register_physical_memory(0x100000 - isa_bios_size,
isa_bios_size,
- (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
+ (bios_offset + bios_size - isa_bios_size) /* | IO_MEM_ROM */);
+
+ if (extboot_drive != -1) {
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, EXTBOOT_FILENAME);
+ option_rom[nb_option_roms++] = buf;
+ }
{
ram_addr_t option_rom_offset;
@@ -913,8 +963,13 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
exit(1);
}
size = (size + 4095) & ~4095;
+ /* XXX: for DDIM support, "ROM space" should be writable during
+ initialization, and (optionally) marked readonly by the BIOS
+ before INT 19h. See the PNPBIOS specification, appendix B.
+ DDIM support is mandatory for proper PCI expansion ROM support. */
cpu_register_physical_memory(0xd0000 + offset,
- size, option_rom_offset | IO_MEM_ROM);
+ size, option_rom_offset /* | IO_MEM_ROM */);
+ option_rom_setup_reset(0xd0000 + offset, size);
offset += size;
}
}
@@ -976,7 +1031,12 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
if (pci_enabled) {
ioapic = ioapic_init();
}
- pit = pit_init(0x40, i8259[0]);
+#ifdef USE_KVM_PIT
+ if (kvm_enabled() && qemu_kvm_pit_in_kernel())
+ pit = kvm_pit_init(0x40, i8259[0]);
+ else
+#endif
+ pit = pit_init(0x40, i8259[0]);
pcspk_init(pit);
if (!no_hpet) {
hpet_init(i8259);
@@ -1003,7 +1063,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
nd = &nd_table[i];
if (!nd->model) {
if (pci_enabled) {
- nd->model = "ne2k_pci";
+ nd->model = "rtl8139";
} else {
nd->model = "ne2k_isa";
}
@@ -1013,7 +1073,8 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
} 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);
+ if (!pci_nic_init(pci_bus, nd, -1))
+ exit(1);
} else if (strcmp(nd->model, "?") == 0) {
fprintf(stderr, "qemu: Supported ISA NICs: ne2k_isa\n");
exit(1);
@@ -1023,6 +1084,8 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
}
}
+ qemu_system_hot_add_init(cpu_model);
+
if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
fprintf(stderr, "qemu: too many IDE bus\n");
exit(1);
@@ -1110,9 +1173,26 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
}
}
+ if (extboot_drive != -1) {
+ DriveInfo *info = &drives_table[extboot_drive];
+ int cyls, heads, secs;
+
+ if (info->type != IF_IDE && info->type != IF_VIRTIO) {
+ bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs);
+ bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs);
+ }
+
+ extboot_init(info->bdrv, 1);
+ }
+
/* Add virtio balloon device */
if (pci_enabled)
virtio_balloon_init(pci_bus);
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+ if (kvm_enabled())
+ add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index);
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
}
static void pc_init_pci(ram_addr_t ram_size, int vga_ram_size,
diff --git a/hw/pc.h b/hw/pc.h
index a60bddf0f..e9ddd4dd3 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -60,6 +60,10 @@ int pit_get_initial_count(PITState *pit, int channel);
int pit_get_mode(PITState *pit, int channel);
int pit_get_out(PITState *pit, int channel, int64_t current_time);
+/* i8254-kvm.c */
+
+PITState *kvm_pit_init(int base, qemu_irq irq);
+
void hpet_pit_disable(void);
void hpet_pit_enable(void);
@@ -92,6 +96,7 @@ extern int fd_bootchk;
void ioport_set_a20(int enable);
int ioport_get_a20(void);
+CPUState *pc_new_cpu(int cpu, const char *cpu_model, int pci_enabled);
/* acpi.c */
extern int acpi_enabled;
@@ -108,6 +113,9 @@ void pcspk_init(PITState *);
int pcspk_audio_init(AudioState *, qemu_irq *pic);
/* piix_pci.c */
+/* config space register for IRQ routing */
+#define PIIX_CONFIG_IRQ_ROUTE 0x60
+
PCIBus *i440fx_init(PCIDevice **pi440fx_state, qemu_irq *pic);
void i440fx_set_smm(PCIDevice *d, int val);
int piix3_init(PCIBus *bus, int devfn);
@@ -116,6 +124,10 @@ void i440fx_init_memory_mappings(PCIDevice *d);
extern PCIDevice *piix4_dev;
int piix4_init(PCIBus *bus, int devfn);
+int piix_get_irq(int pin);
+
+int ipf_map_irq(PCIDevice *pci_dev, int irq_num);
+
/* vga.c */
enum vga_retrace_method {
VGA_RETRACE_DUMB,
@@ -125,9 +137,9 @@ enum vga_retrace_method {
extern enum vga_retrace_method vga_retrace_method;
#ifndef TARGET_SPARC
-#define VGA_RAM_SIZE (8192 * 1024)
+#define VGA_RAM_SIZE (16 * 1024 * 1024)
#else
-#define VGA_RAM_SIZE (9 * 1024 * 1024)
+#define VGA_RAM_SIZE (17 * 1024 * 1024)
#endif
int isa_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
@@ -160,4 +172,8 @@ void pci_piix4_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd);
+/* extboot.c */
+
+void extboot_init(BlockDriverState *bs, int cmd);
+
#endif
diff --git a/hw/pci.c b/hw/pci.c
index 8252d21b9..8589dfa8c 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -26,6 +26,8 @@
#include "console.h"
#include "net.h"
#include "virtio-net.h"
+#include "pc.h"
+#include "qemu-kvm.h"
//#define DEBUG_PCI
@@ -49,6 +51,7 @@ struct PCIBus {
static void pci_update_mappings(PCIDevice *d);
static void pci_set_irq(void *opaque, int irq_num, int level);
+void assigned_dev_update_irq(PCIDevice *d);
target_phys_addr_t pci_mem_base;
static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
@@ -198,6 +201,48 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
return pci_dev;
}
+static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
+{
+ return addr + pci_mem_base;
+}
+
+static void pci_unregister_io_regions(PCIDevice *pci_dev)
+{
+ PCIIORegion *r;
+ int i;
+
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &pci_dev->io_regions[i];
+ if (!r->size)
+ continue;
+ if (r->type == PCI_ADDRESS_SPACE_IO) {
+ isa_unassign_ioport(r->addr, r->size);
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
+ r->size,
+ IO_MEM_UNASSIGNED);
+ }
+ }
+}
+
+int pci_unregister_device(PCIDevice *pci_dev)
+{
+ int ret = 0;
+
+ if (pci_dev->unregister)
+ ret = pci_dev->unregister(pci_dev);
+ if (ret)
+ return ret;
+
+ pci_unregister_io_regions(pci_dev);
+
+ qemu_free_irqs(pci_dev->irq);
+ pci_irq_index--;
+ pci_dev->bus->devices[pci_dev->devfn] = NULL;
+ qemu_free(pci_dev);
+ return 0;
+}
+
void pci_register_io_region(PCIDevice *pci_dev, int region_num,
uint32_t size, int type,
PCIMapIORegionFunc *map_func)
@@ -207,6 +252,11 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num,
if ((unsigned int)region_num >= PCI_NUM_REGIONS)
return;
+
+ if (size & (size-1))
+ term_printf("WARNING: PCI region size must be pow2 "
+ "type=0x%x, size=0x%x\n", type, size);
+
r = &pci_dev->io_regions[region_num];
r->addr = -1;
r->size = size;
@@ -220,10 +270,6 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num,
*(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
}
-static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
-{
- return addr + pci_mem_base;
-}
static void pci_update_mappings(PCIDevice *d)
{
@@ -434,6 +480,13 @@ void pci_default_write_config(PCIDevice *d,
val >>= 8;
}
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel() &&
+ address >= PIIX_CONFIG_IRQ_ROUTE &&
+ address < PIIX_CONFIG_IRQ_ROUTE + 4)
+ assigned_dev_update_irq(d);
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+
end = address + len;
if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) {
/* if the command register is modified, we must modify the mappings */
@@ -525,6 +578,11 @@ static void pci_set_irq(void *opaque, int irq_num, int level)
return;
pci_dev->irq_state[irq_num] = level;
+
+#if defined(TARGET_IA64)
+ ioapic_set_irq(pci_dev, irq_num, level);
+#endif
+
for (;;) {
bus = pci_dev->bus;
irq_num = bus->map_irq(pci_dev, irq_num);
@@ -536,6 +594,11 @@ static void pci_set_irq(void *opaque, int irq_num, int level)
bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
}
+int pci_map_irq(PCIDevice *pci_dev, int pin)
+{
+ return pci_dev->bus->map_irq(pci_dev, pin);
+}
+
/***********************************************************/
/* monitor info on PCI */
@@ -653,32 +716,42 @@ void pci_info(void)
}
/* Initialize a PCI NIC. */
-void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
+PCIDevice *pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
{
+ PCIDevice *pci_dev;
+
if (strcmp(nd->model, "ne2k_pci") == 0) {
- pci_ne2000_init(bus, nd, devfn);
+ pci_dev = pci_ne2000_init(bus, nd, devfn);
} else if (strcmp(nd->model, "i82551") == 0) {
- pci_i82551_init(bus, nd, devfn);
+ pci_dev = pci_i82551_init(bus, nd, devfn);
} else if (strcmp(nd->model, "i82557b") == 0) {
- pci_i82557b_init(bus, nd, devfn);
+ pci_dev = pci_i82557b_init(bus, nd, devfn);
} else if (strcmp(nd->model, "i82559er") == 0) {
- pci_i82559er_init(bus, nd, devfn);
+ pci_dev = pci_i82559er_init(bus, nd, devfn);
} else if (strcmp(nd->model, "rtl8139") == 0) {
- pci_rtl8139_init(bus, nd, devfn);
+ pci_dev = pci_rtl8139_init(bus, nd, devfn);
} else if (strcmp(nd->model, "e1000") == 0) {
- pci_e1000_init(bus, nd, devfn);
+ pci_dev = pci_e1000_init(bus, nd, devfn);
} else if (strcmp(nd->model, "pcnet") == 0) {
- pci_pcnet_init(bus, nd, devfn);
+ pci_dev = pci_pcnet_init(bus, nd, devfn);
} else if (strcmp(nd->model, "virtio") == 0) {
- virtio_net_init(bus, nd, devfn);
+ pci_dev = virtio_net_init(bus, nd, devfn);
} else if (strcmp(nd->model, "?") == 0) {
fprintf(stderr, "qemu: Supported PCI NICs: i82551 i82557b i82559er"
" ne2k_pci pcnet rtl8139 e1000 virtio\n");
- exit (1);
+ return NULL;
} else {
fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
- exit (1);
+ return NULL;
}
+
+ if (!pci_dev) {
+ fprintf(stderr, "qemu: Unable to initialze NIC: %s\n", nd->model);
+ return NULL;
+ }
+
+ nd->devfn = pci_dev->devfn;
+ return pci_dev;
}
typedef struct {
@@ -703,6 +776,33 @@ static void pci_bridge_write_config(PCIDevice *d,
pci_default_write_config(d, address, val, len);
}
+PCIBus *pci_find_bus(int bus_num)
+{
+ PCIBus *bus = first_bus;
+
+ while (bus && bus->bus_num != bus_num)
+ bus = bus->next;
+
+ return bus;
+}
+
+PCIDevice *pci_find_device(int bus_num, int slot)
+{
+ int devfn;
+ PCIDevice *d;
+ PCIBus *bus = pci_find_bus(bus_num);
+
+ if (!bus)
+ return NULL;
+
+ for(devfn = 0; devfn < 256; devfn++) {
+ d = bus->devices[devfn];
+ if (d && PCI_SLOT(devfn) == slot)
+ return d;
+ }
+ return NULL;
+}
+
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
pci_map_irq_fn map_irq, const char *name)
{
diff --git a/hw/pci.h b/hw/pci.h
index 3b1caf5ca..1f33819fe 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -4,8 +4,11 @@
/* PCI includes legacy ISA access. */
#include "isa.h"
-/* PCI bus */
+/* imported from <linux/pci.h> */
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+/* PCI bus */
extern target_phys_addr_t pci_mem_base;
/* see pci-ids.txt */
@@ -23,6 +26,7 @@ typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
uint32_t address, int len);
typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
uint32_t addr, uint32_t size, int type);
+typedef int PCIUnregisterFunc(PCIDevice *pci_dev);
#define PCI_ADDRESS_SPACE_MEM 0x00
#define PCI_ADDRESS_SPACE_IO 0x01
@@ -87,6 +91,7 @@ struct PCIDevice {
/* do not access the following fields */
PCIConfigReadFunc *config_read;
PCIConfigWriteFunc *config_write;
+ PCIUnregisterFunc *unregister;
/* ??? This is a PC-specific hack, and should be removed. */
int irq_index;
@@ -102,10 +107,13 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
PCIConfigReadFunc *config_read,
PCIConfigWriteFunc *config_write);
+int pci_unregister_device(PCIDevice *pci_dev);
+
void pci_register_io_region(PCIDevice *pci_dev, int region_num,
uint32_t size, int type,
PCIMapIORegionFunc *map_func);
+int pci_map_irq(PCIDevice *pci_dev, int pin);
uint32_t pci_default_read_config(PCIDevice *d,
uint32_t address, int len);
void pci_default_write_config(PCIDevice *d,
@@ -118,11 +126,13 @@ typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
qemu_irq *pic, int devfn_min, int nirq);
-void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn);
void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len);
uint32_t pci_data_read(void *opaque, uint32_t addr, int len);
int pci_bus_num(PCIBus *s);
void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d));
+PCIBus *pci_find_bus(int bus_num);
+PCIDevice *pci_find_device(int bus_num, int slot);
void pci_info(void);
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
@@ -146,23 +156,23 @@ void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn);
/* eepro100.c */
-void pci_i82551_init(PCIBus *bus, NICInfo *nd, int devfn);
-void pci_i82557b_init(PCIBus *bus, NICInfo *nd, int devfn);
-void pci_i82559er_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_i82551_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_i82557b_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_i82559er_init(PCIBus *bus, NICInfo *nd, int devfn);
/* ne2000.c */
-void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn);
/* rtl8139.c */
-void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn);
/* e1000.c */
-void pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn);
/* pcnet.c */
-void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn);
+PCIDevice *pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn);
/* prep_pci.c */
PCIBus *pci_prep_init(qemu_irq *pic);
diff --git a/hw/pcnet.c b/hw/pcnet.c
index 30c453c93..c70239bdb 100644
--- a/hw/pcnet.c
+++ b/hw/pcnet.c
@@ -1992,7 +1992,7 @@ static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr,
cpu_physical_memory_read(addr, buf, len);
}
-void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn)
+PCIDevice *pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn)
{
PCNetState *d;
uint8_t *pci_conf;
@@ -2004,6 +2004,8 @@ void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn)
d = (PCNetState *)pci_register_device(bus, "PCNet", sizeof(PCNetState),
devfn, NULL, NULL);
+ if (!d)
+ return NULL;
pci_conf = d->dev.config;
@@ -2040,6 +2042,7 @@ void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn)
d->pci_dev = &d->dev;
pcnet_common_init(d, nd, "pcnet");
+ return (PCIDevice *)d;
}
/* SPARC32 interface */
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 1d6bf2017..2a4e265f4 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -26,6 +26,8 @@
#include "pc.h"
#include "pci.h"
+#include "qemu-kvm.h"
+
typedef uint32_t pci_addr_t;
#include "pci_host.h"
@@ -91,6 +93,10 @@ static void i440fx_update_memory_mappings(PCIDevice *d)
int i, r;
uint32_t smram, addr;
+ if (kvm_enabled()) {
+ /* FIXME: Support remappings and protection changes. */
+ return;
+ }
update_pam(d, 0xf0000, 0x100000, (d->config[0x59] >> 4) & 3);
for(i = 0; i < 12; i++) {
r = (d->config[(i >> 1) + 0x5a] >> ((i & 1) * 4)) & 3;
@@ -237,6 +243,16 @@ static void piix3_set_irq(qemu_irq *pic, int irq_num, int level)
}
}
+int piix_get_irq(int pin)
+{
+ if (piix3_dev)
+ return piix3_dev->config[0x60+pin];
+ if (piix4_dev)
+ return piix4_dev->config[0x60+pin];
+
+ return 0;
+}
+
static void piix3_reset(PCIDevice *d)
{
uint8_t *pci_conf = d->config;
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
index 7832cd9f3..25a91bdf8 100644
--- a/hw/ppc4xx.h
+++ b/hw/ppc4xx.h
@@ -3,6 +3,9 @@
*
* Copyright (c) 2007 Jocelyn Mayer
*
+ * Copyright 2008 IBM Corp.
+ * Authors: Hollis Blanchard <hollisb@us.ibm.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
diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c
index 5bdb80536..daa3aeb95 100644
--- a/hw/ppc_chrp.c
+++ b/hw/ppc_chrp.c
@@ -267,7 +267,8 @@ static void ppc_core99_init (ram_addr_t ram_size, int vga_ram_size,
for(i = 0; i < nb_nics; i++) {
if (!nd_table[i].model)
nd_table[i].model = "ne2k_pci";
- pci_nic_init(pci_bus, &nd_table[i], -1);
+ if (!pci_nic_init(pci_bus, &nd_table[i], -1))
+ exit(1);
}
if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
fprintf(stderr, "qemu: too many IDE bus\n");
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index 571c48e13..e315028a5 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -675,7 +675,8 @@ static void ppc_prep_init (ram_addr_t ram_size, int vga_ram_size,
|| strcmp(nd_table[i].model, "ne2k_isa") == 0) {
isa_ne2000_init(ne2000_io[i], i8259[ne2000_irq[i]], &nd_table[i]);
} else {
- pci_nic_init(pci_bus, &nd_table[i], -1);
+ if (!pci_nic_init(pci_bus, &nd_table[i], -1))
+ exit(1);
}
}
diff --git a/hw/realview.c b/hw/realview.c
index a9d20edc5..eb98099c0 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -126,7 +126,8 @@ static void realview_init(ram_addr_t ram_size, int vga_ram_size,
if (strcmp(nd->model, "smc91c111") == 0) {
smc91c111_init(nd, 0x4e000000, pic[28]);
} else {
- pci_nic_init(pci_bus, nd, -1);
+ if (!pci_nic_init(pci_bus, nd, -1))
+ exit(1);
}
}
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index c3ab8543f..ed80fdea8 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -3400,7 +3400,7 @@ static void rtl8139_timer(void *opaque)
}
#endif /* RTL8139_ONBOARD_TIMER */
-void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
+PCIDevice *pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
{
PCIRTL8139State *d;
RTL8139State *s;
@@ -3410,6 +3410,9 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
"RTL8139", sizeof(PCIRTL8139State),
devfn,
NULL, NULL);
+ if (!d)
+ return NULL;
+
pci_conf = d->dev.config;
pci_conf[0x00] = 0xec; /* Realtek 8139 */
pci_conf[0x01] = 0x10;
@@ -3462,4 +3465,5 @@ void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn)
qemu_mod_timer(s->timer,
rtl8139_get_next_tctr_time(s,qemu_get_clock(vm_clock)));
#endif /* RTL8139_ONBOARD_TIMER */
+ return (PCIDevice *)d;
}
diff --git a/hw/sun4u.c b/hw/sun4u.c
index b3ecc1e34..0c1e1a24c 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -491,7 +491,8 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size,
for(i = 0; i < nb_nics; i++) {
if (!nd_table[i].model)
nd_table[i].model = "ne2k_pci";
- pci_nic_init(pci_bus, &nd_table[i], -1);
+ if (!pci_nic_init(pci_bus, &nd_table[i], -1))
+ exit(1);
}
irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index e5d02f2bb..527f65eff 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -199,7 +199,8 @@ static void versatile_init(ram_addr_t ram_size, int vga_ram_size,
if (strcmp(nd->model, "smc91c111") == 0) {
smc91c111_init(nd, 0x10010000, sic[25]);
} else {
- pci_nic_init(pci_bus, nd, -1);
+ if (!pci_nic_init(pci_bus, nd, -1))
+ exit(1);
}
}
if (usb_enabled) {
diff --git a/hw/vga.c b/hw/vga.c
index 29354f495..f1e50006b 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -29,6 +29,7 @@
#include "pixel_ops.h"
#include "qemu-timer.h"
#include "kvm.h"
+#include "qemu-kvm.h"
//#define DEBUG_VGA
//#define DEBUG_VGA_MEM
@@ -1576,11 +1577,12 @@ static void vga_sync_dirty_bitmap(VGAState *s)
*/
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, bits;
+ int y1, y, update, linesize, y_start, double_scan, mask;
+ int width, height, shift_control, line_offset, bwidth, bits;
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;
full_update |= update_basic_params(s);
@@ -1807,6 +1809,9 @@ static void vga_update_display(void *opaque)
vga_draw_text(s, full_update);
break;
case GMODE_GRAPH:
+#ifdef TARGET_IA64
+ full_update = 1;
+#endif
vga_draw_graphic(s, full_update);
break;
case GMODE_BLANK:
@@ -2112,26 +2117,46 @@ typedef struct PCIVGAState {
VGAState vga_state;
} PCIVGAState;
+static int s1, s2;
+
+static void mark_dirty(target_phys_addr_t start, target_phys_addr_t len)
+{
+ target_phys_addr_t end = start + len;
+
+ while (start < end) {
+ cpu_physical_memory_set_dirty(cpu_get_physical_page_desc(start));
+ start += TARGET_PAGE_SIZE;
+ }
+}
+
void vga_dirty_log_start(VGAState *s)
{
if (kvm_enabled() && s->map_addr)
- kvm_log_start(s->map_addr, s->map_end - s->map_addr);
-
+ if (!s1) {
+ kvm_log_start(s->map_addr, s->map_end - s->map_addr);
+ mark_dirty(s->map_addr, s->map_end - s->map_addr);
+ s1 = 1;
+ }
if (kvm_enabled() && s->lfb_vram_mapped) {
- kvm_log_start(isa_mem_base + 0xa0000, 0x8000);
- kvm_log_start(isa_mem_base + 0xa8000, 0x8000);
+ if (!s2) {
+ kvm_log_start(isa_mem_base + 0xa0000, 0x8000);
+ kvm_log_start(isa_mem_base + 0xa8000, 0x8000);
+ mark_dirty(isa_mem_base + 0xa0000, 0x10000);
+ }
+ s2 = 1;
}
}
void vga_dirty_log_stop(VGAState *s)
{
- if (kvm_enabled() && s->map_addr)
+ if (kvm_enabled() && s->map_addr && s1)
kvm_log_stop(s->map_addr, s->map_end - s->map_addr);
- if (kvm_enabled() && s->lfb_vram_mapped) {
+ if (kvm_enabled() && s->lfb_vram_mapped && s2) {
kvm_log_stop(isa_mem_base + 0xa0000, 0x8000);
kvm_log_stop(isa_mem_base + 0xa8000, 0x8000);
}
+ s1 = s2 = 0;
}
static void vga_map(PCIDevice *pci_dev, int region_num,
@@ -2151,6 +2176,139 @@ static void vga_map(PCIDevice *pci_dev, int region_num,
vga_dirty_log_start(s);
}
+#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,
ram_addr_t vga_ram_offset, int vga_ram_size)
{
@@ -2204,6 +2362,9 @@ void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
memset(&s->retrace_info, 0, sizeof (s->retrace_info));
break;
}
+#ifdef TARGET_IA64
+ vga_bios_init(s);
+#endif
}
/* used by both ISA and PCI */
@@ -2382,6 +2543,19 @@ int isa_vga_mm_init(DisplayState *ds, uint8_t *vga_ram_base,
return 0;
}
+static void pci_vga_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ PCIVGAState *pvs = container_of(d, PCIVGAState, dev);
+ VGAState *s = &pvs->vga_state;
+
+ vga_dirty_log_stop(s);
+ pci_default_write_config(d, address, val, len);
+ if (s->map_addr && pvs->dev.io_regions[0].addr == -1)
+ s->map_addr = 0;
+ vga_dirty_log_start(s);
+}
+
int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
unsigned long vga_ram_offset, int vga_ram_size,
unsigned long vga_bios_offset, int vga_bios_size)
@@ -2392,7 +2566,7 @@ int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
d = (PCIVGAState *)pci_register_device(bus, "VGA",
sizeof(PCIVGAState),
- -1, NULL, NULL);
+ -1, NULL, pci_vga_write_config);
if (!d)
return -1;
s = &d->vga_state;
@@ -2487,11 +2661,11 @@ int ppm_save(const char *filename, uint8_t *data,
static void vga_screen_dump(void *opaque, const char *filename)
{
VGAState *s = (VGAState *)opaque;
- DisplayState *saved_ds, ds1, *ds = &ds1;
+ DisplayState saved_ds, *ds = s->ds;
/* XXX: this is a little hackish */
vga_invalidate_display(s);
- saved_ds = s->ds;
+ saved_ds = *s->ds;
memset(ds, 0, sizeof(DisplayState));
ds->dpy_update = vga_save_dpy_update;
@@ -2499,7 +2673,6 @@ static void vga_screen_dump(void *opaque, const char *filename)
ds->dpy_refresh = vga_save_dpy_refresh;
ds->depth = 32;
- s->ds = ds;
s->graphic_mode = -1;
vga_update_display(s);
@@ -2508,5 +2681,5 @@ static void vga_screen_dump(void *opaque, const char *filename)
ds_get_linesize(s->ds));
qemu_free(ds_get_data(ds));
}
- s->ds = saved_ds;
+ *s->ds = saved_ds;
}
diff --git a/hw/vga_int.h b/hw/vga_int.h
index 0c6ae7e71..65ac68acf 100644
--- a/hw/vga_int.h
+++ b/hw/vga_int.h
@@ -30,8 +30,8 @@
/* bochs VBE support */
#define CONFIG_BOCHS_VBE
-#define VBE_DISPI_MAX_XRES 1600
-#define VBE_DISPI_MAX_YRES 1200
+#define VBE_DISPI_MAX_XRES 2560
+#define VBE_DISPI_MAX_YRES 1600
#define VBE_DISPI_MAX_BPP 32
#define VBE_DISPI_INDEX_ID 0x0
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
index 07655168d..24d387f31 100644
--- a/hw/virtio-balloon.c
+++ b/hw/virtio-balloon.c
@@ -19,6 +19,7 @@
#include "balloon.h"
#include "virtio-balloon.h"
#include "kvm.h"
+#include "qemu-kvm.h"
#if defined(__linux__)
#include <sys/mman.h>
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index e654cc58a..bcc58548e 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -237,6 +237,7 @@ void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs)
s->vdev.get_features = virtio_blk_get_features;
s->vdev.reset = virtio_blk_reset;
s->bs = bs;
+ bs->devfn = s->vdev.pci_dev.devfn;
bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
bdrv_set_geometry_hint(s->bs, cylinders, heads, secs);
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 1f45b2d2a..ef8f59181 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -15,6 +15,11 @@
#include "net.h"
#include "qemu-timer.h"
#include "virtio-net.h"
+#ifdef USE_KVM
+#include "qemu-kvm.h"
+#endif
+
+#define TAP_VNET_HDR
typedef struct VirtIONet
{
@@ -49,6 +54,24 @@ static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
{
uint32_t features = (1 << VIRTIO_NET_F_MAC);
+#ifdef TAP_VNET_HDR
+ VirtIONet *n = to_virtio_net(vdev);
+ VLANClientState *host = n->vc->vlan->first_client;
+
+ if (tap_has_vnet_hdr(host)) {
+ tap_using_vnet_hdr(host, 1);
+ features |= (1 << VIRTIO_NET_F_CSUM);
+ features |= (1 << VIRTIO_NET_F_GUEST_CSUM);
+ features |= (1 << VIRTIO_NET_F_GUEST_TSO4);
+ features |= (1 << VIRTIO_NET_F_GUEST_TSO6);
+ features |= (1 << VIRTIO_NET_F_GUEST_ECN);
+ features |= (1 << VIRTIO_NET_F_HOST_TSO4);
+ features |= (1 << VIRTIO_NET_F_HOST_TSO6);
+ features |= (1 << VIRTIO_NET_F_HOST_ECN);
+ features |= (1 << VIRTIO_NET_F_MRG_RXBUF);
+ /* Kernel can't actually handle UFO in software currently. */
+ }
+#endif
return features;
}
@@ -56,14 +79,34 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev)
static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
{
VirtIONet *n = to_virtio_net(vdev);
+#ifdef TAP_VNET_HDR
+ VLANClientState *host = n->vc->vlan->first_client;
+#endif
n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
+
+#ifdef TAP_VNET_HDR
+ if (!tap_has_vnet_hdr(host) || !host->set_offload)
+ return;
+
+ host->set_offload(host,
+ (features >> VIRTIO_NET_F_GUEST_CSUM) & 1,
+ (features >> VIRTIO_NET_F_GUEST_TSO4) & 1,
+ (features >> VIRTIO_NET_F_GUEST_TSO6) & 1,
+ (features >> VIRTIO_NET_F_GUEST_ECN) & 1);
+#endif
}
/* RX */
static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
{
+#ifdef USE_KVM
+ /* We now have RX buffers, signal to the IO thread to break out of the
+ select to re-poll the tap file descriptor */
+ if (kvm_enabled())
+ qemu_kvm_notify_work();
+#endif
}
static int do_virtio_net_can_receive(VirtIONet *n, int bufsize)
@@ -90,6 +133,36 @@ static int virtio_net_can_receive(void *opaque)
return do_virtio_net_can_receive(n, VIRTIO_NET_MAX_BUFSIZE);
}
+#ifdef TAP_VNET_HDR
+/* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
+ * it never finds out that the packets don't have valid checksums. This
+ * causes dhclient to get upset. Fedora's carried a patch for ages to
+ * fix this with Xen but it hasn't appeared in an upstream release of
+ * dhclient yet.
+ *
+ * To avoid breaking existing guests, we catch udp packets and add
+ * checksums. This is terrible but it's better than hacking the guest
+ * kernels.
+ *
+ * N.B. if we introduce a zero-copy API, this operation is no longer free so
+ * we should provide a mechanism to disable it to avoid polluting the host
+ * cache.
+ */
+static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
+ const uint8_t *buf, size_t size)
+{
+ if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
+ (size > 27 && size < 1500) && /* normal sized MTU */
+ (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
+ (buf[23] == 17) && /* ip.protocol == UDP */
+ (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
+ /* FIXME this cast is evil */
+ net_checksum_calculate((uint8_t *)buf, size);
+ hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
+ }
+}
+#endif
+
static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)
{
int offset, i;
@@ -114,6 +187,14 @@ static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
hdr->flags = 0;
hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
+#ifdef TAP_VNET_HDR
+ if (tap_has_vnet_hdr(n->vc->vlan->first_client)) {
+ memcpy(hdr, buf, sizeof(*hdr));
+ offset = sizeof(*hdr);
+ work_around_broken_dhclient(hdr, buf + offset, size - offset);
+ }
+#endif
+
/* We only ever receive a struct virtio_net_hdr from the tapfd,
* but we may be passing along a larger header to the guest.
*/
@@ -196,7 +277,11 @@ static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
{
VirtQueueElement elem;
+#ifdef TAP_VNET_HDR
+ int has_vnet_hdr = tap_has_vnet_hdr(n->vc->vlan->first_client);
+#else
int has_vnet_hdr = 0;
+#endif
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
return;
diff --git a/hw/virtio.c b/hw/virtio.c
index dba80f81c..35f46696f 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -16,7 +16,7 @@
#include "virtio.h"
#include "sysemu.h"
-//#define VIRTIO_ZERO_COPY
+#define VIRTIO_ZERO_COPY
/* from Linux's linux/virtio_pci.h */
@@ -299,7 +299,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
qemu_free(elem->in_sg[i].iov_base);
#endif
-
+
offset += size;
}
diff --git a/hw/vmport.c b/hw/vmport.c
index 884af3fd9..648861b4d 100644
--- a/hw/vmport.c
+++ b/hw/vmport.c
@@ -21,10 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+
#include "hw.h"
#include "isa.h"
#include "pc.h"
#include "sysemu.h"
+#include "qemu-kvm.h"
//#define VMPORT_DEBUG
@@ -57,6 +59,10 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
CPUState *env = cpu_single_env;
unsigned char command;
uint32_t eax;
+ uint32_t ret;
+
+ if (kvm_enabled())
+ kvm_save_registers(env);
eax = env->regs[R_EAX];
if (eax != VMPORT_MAGIC)
@@ -73,7 +79,12 @@ static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
return eax;
}
- return s->func[command](s->opaque[command], addr);
+ ret = s->func[command](s->opaque[command], addr);
+
+ if (kvm_enabled())
+ kvm_load_registers(env);
+
+ return ret;
}
static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val)
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-tpr-opt.c b/kvm-tpr-opt.c
new file mode 100644
index 000000000..f2a3a1e6e
--- /dev/null
+++ b/kvm-tpr-opt.c
@@ -0,0 +1,378 @@
+/*
+ * tpr optimization for qemu/kvm
+ *
+ * Copyright (C) 2007-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+
+#include "config.h"
+#include "config-host.h"
+
+#include <string.h>
+
+#include "hw/hw.h"
+#include "hw/isa.h"
+#include "sysemu.h"
+#include "qemu-kvm.h"
+#include "cpu.h"
+
+#include <stdio.h>
+
+extern kvm_context_t kvm_context;
+
+static uint64_t map_addr(struct kvm_sregs *sregs, target_ulong virt, unsigned *perms)
+{
+ uint64_t mask = ((1ull << 48) - 1) & ~4095ull;
+ uint64_t p, pp = 7;
+
+ p = sregs->cr3;
+ if (sregs->cr4 & 0x20) {
+ p &= ~31ull;
+ p = ldq_phys(p + 8 * (virt >> 30));
+ if (!(p & 1))
+ return -1ull;
+ p &= mask;
+ p = ldq_phys(p + 8 * ((virt >> 21) & 511));
+ if (!(p & 1))
+ return -1ull;
+ pp &= p;
+ if (p & 128) {
+ p += ((virt >> 12) & 511) << 12;
+ } else {
+ p &= mask;
+ p = ldq_phys(p + 8 * ((virt >> 12) & 511));
+ if (!(p & 1))
+ return -1ull;
+ pp &= p;
+ }
+ } else {
+ p &= mask;
+ p = ldl_phys(p + 4 * ((virt >> 22) & 1023));
+ if (!(p & 1))
+ return -1ull;
+ pp &= p;
+ if (p & 128) {
+ p += ((virt >> 12) & 1023) << 12;
+ } else {
+ p &= mask;
+ p = ldl_phys(p + 4 * ((virt >> 12) & 1023));
+ pp &= p;
+ if (!(p & 1))
+ return -1ull;
+ }
+ }
+ if (perms)
+ *perms = pp >> 1;
+ p &= mask;
+ return p + (virt & 4095);
+}
+
+static uint8_t read_byte_virt(CPUState *env, target_ulong virt)
+{
+ struct kvm_sregs sregs;
+
+ kvm_get_sregs(kvm_context, env->cpu_index, &sregs);
+ return ldub_phys(map_addr(&sregs, virt, NULL));
+}
+
+static void write_byte_virt(CPUState *env, target_ulong virt, uint8_t b)
+{
+ struct kvm_sregs sregs;
+
+ kvm_get_sregs(kvm_context, env->cpu_index, &sregs);
+ stb_phys(map_addr(&sregs, virt, NULL), b);
+}
+
+static __u64 kvm_rsp_read(CPUState *env)
+{
+ struct kvm_regs regs;
+
+ kvm_get_regs(kvm_context, env->cpu_index, &regs);
+ return regs.rsp;
+}
+
+struct vapic_bios {
+ char signature[8];
+ uint32_t virt_base;
+ uint32_t fixup_start;
+ uint32_t fixup_end;
+ uint32_t vapic;
+ uint32_t vapic_size;
+ uint32_t vcpu_shift;
+ uint32_t real_tpr;
+ struct vapic_patches {
+ uint32_t set_tpr;
+ uint32_t set_tpr_eax;
+ uint32_t get_tpr[8];
+ uint32_t get_tpr_stack;
+ } __attribute__((packed)) up, mp;
+} __attribute__((packed));
+
+static struct vapic_bios vapic_bios;
+
+static uint32_t real_tpr;
+static uint32_t bios_addr;
+static uint32_t vapic_phys;
+static int bios_enabled;
+static uint32_t vbios_desc_phys;
+
+void update_vbios_real_tpr()
+{
+ cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 0);
+ vapic_bios.real_tpr = real_tpr;
+ vapic_bios.vcpu_shift = 7;
+ cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 1);
+}
+
+static unsigned modrm_reg(uint8_t modrm)
+{
+ return (modrm >> 3) & 7;
+}
+
+static int is_abs_modrm(uint8_t modrm)
+{
+ return (modrm & 0xc7) == 0x05;
+}
+
+static int instruction_is_ok(CPUState *env, uint64_t rip, int is_write)
+{
+ uint8_t b1, b2;
+ unsigned addr_offset;
+ uint32_t addr;
+ uint64_t p;
+
+ if ((rip & 0xf0000000) != 0x80000000 && (rip & 0xf0000000) != 0xe0000000)
+ return 0;
+ if (kvm_rsp_read(env) == 0)
+ return 0;
+ b1 = read_byte_virt(env, rip);
+ b2 = read_byte_virt(env, rip + 1);
+ switch (b1) {
+ case 0xc7: /* mov imm32, r/m32 (c7/0) */
+ if (modrm_reg(b2) != 0)
+ return 0;
+ /* fall through */
+ case 0x89: /* mov r32 to r/m32 */
+ case 0x8b: /* mov r/m32 to r32 */
+ if (!is_abs_modrm(b2))
+ return 0;
+ addr_offset = 2;
+ break;
+ case 0xa1: /* mov abs to eax */
+ case 0xa3: /* mov eax to abs */
+ addr_offset = 1;
+ break;
+ case 0xff: /* push r/m32 */
+ if (modrm_reg(b2) != 6 || !is_abs_modrm(b2))
+ return 0;
+ addr_offset = 2;
+ default:
+ return 0;
+ }
+ p = rip + addr_offset;
+ addr = read_byte_virt(env, p++);
+ addr |= read_byte_virt(env, p++) << 8;
+ addr |= read_byte_virt(env, p++) << 16;
+ addr |= read_byte_virt(env, p++) << 24;
+ if ((addr & 0xfff) != 0x80)
+ return 0;
+ real_tpr = addr;
+ update_vbios_real_tpr();
+ return 1;
+}
+
+static int bios_is_mapped(CPUState *env, uint64_t rip)
+{
+ uint32_t probe;
+ uint64_t phys;
+ struct kvm_sregs sregs;
+ unsigned perms;
+ uint32_t i;
+ uint32_t offset, fixup;
+
+ if (bios_enabled)
+ return 1;
+
+ kvm_get_sregs(kvm_context, env->cpu_index, &sregs);
+
+ probe = (rip & 0xf0000000) + 0xe0000;
+ phys = map_addr(&sregs, probe, &perms);
+ if (phys != 0xe0000)
+ return 0;
+ bios_addr = probe;
+ for (i = 0; i < 64; ++i) {
+ cpu_physical_memory_read(phys, (void *)&vapic_bios, sizeof(vapic_bios));
+ if (memcmp(vapic_bios.signature, "kvm aPiC", 8) == 0)
+ break;
+ phys += 1024;
+ bios_addr += 1024;
+ }
+ if (i == 64)
+ return 0;
+ if (bios_addr == vapic_bios.virt_base)
+ return 1;
+ vbios_desc_phys = phys;
+ for (i = vapic_bios.fixup_start; i < vapic_bios.fixup_end; i += 4) {
+ offset = ldl_phys(phys + i - vapic_bios.virt_base);
+ fixup = phys + offset;
+ stl_phys(fixup, ldl_phys(fixup) + bios_addr - vapic_bios.virt_base);
+ }
+ vapic_phys = vapic_bios.vapic - vapic_bios.virt_base + phys;
+ return 1;
+}
+
+static int enable_vapic(CPUState *env)
+{
+ struct kvm_sregs sregs;
+ static uint8_t one = 1;
+
+ kvm_enable_vapic(kvm_context, env->cpu_index,
+ vapic_phys + (env->cpu_index << 7));
+ cpu_physical_memory_rw(vapic_phys + (env->cpu_index << 7) + 4, &one, 1, 1);
+ bios_enabled = 1;
+
+ return 1;
+}
+
+static void patch_call(CPUState *env, uint64_t rip, uint32_t target)
+{
+ uint32_t offset;
+
+ offset = target - vapic_bios.virt_base + bios_addr - rip - 5;
+ write_byte_virt(env, rip, 0xe8); /* call near */
+ write_byte_virt(env, rip + 1, offset);
+ write_byte_virt(env, rip + 2, offset >> 8);
+ write_byte_virt(env, rip + 3, offset >> 16);
+ write_byte_virt(env, rip + 4, offset >> 24);
+}
+
+static void patch_instruction(CPUState *env, uint64_t rip)
+{
+ uint8_t b1, b2;
+ struct vapic_patches *vp;
+
+ vp = smp_cpus == 1 ? &vapic_bios.up : &vapic_bios.mp;
+ b1 = read_byte_virt(env, rip);
+ b2 = read_byte_virt(env, rip + 1);
+ switch (b1) {
+ case 0x89: /* mov r32 to r/m32 */
+ write_byte_virt(env, rip, 0x50 + modrm_reg(b2)); /* push reg */
+ patch_call(env, rip + 1, vp->set_tpr);
+ break;
+ case 0x8b: /* mov r/m32 to r32 */
+ write_byte_virt(env, rip, 0x90);
+ patch_call(env, rip + 1, vp->get_tpr[modrm_reg(b2)]);
+ break;
+ case 0xa1: /* mov abs to eax */
+ patch_call(env, rip, vp->get_tpr[0]);
+ break;
+ case 0xa3: /* mov eax to abs */
+ patch_call(env, rip, vp->set_tpr_eax);
+ break;
+ case 0xc7: /* mov imm32, r/m32 (c7/0) */
+ write_byte_virt(env, rip, 0x68); /* push imm32 */
+ write_byte_virt(env, rip + 1, read_byte_virt(env, rip+6));
+ write_byte_virt(env, rip + 2, read_byte_virt(env, rip+7));
+ write_byte_virt(env, rip + 3, read_byte_virt(env, rip+8));
+ write_byte_virt(env, rip + 4, read_byte_virt(env, rip+9));
+ patch_call(env, rip + 5, vp->set_tpr);
+ break;
+ case 0xff: /* push r/m32 */
+ printf("patching push\n");
+ write_byte_virt(env, rip, 0x50); /* push eax */
+ patch_call(env, rip + 1, vp->get_tpr_stack);
+ break;
+ default:
+ printf("funny insn %02x %02x\n", b1, b2);
+ }
+}
+
+void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write)
+{
+ if (!instruction_is_ok(env, rip, is_write))
+ return;
+ if (!bios_is_mapped(env, rip))
+ return;
+ if (!enable_vapic(env))
+ return;
+ patch_instruction(env, rip);
+}
+
+void kvm_tpr_vcpu_start(CPUState *env)
+{
+ kvm_enable_tpr_access_reporting(kvm_context, env->cpu_index);
+ if (bios_enabled)
+ enable_vapic(env);
+}
+
+static void tpr_save(QEMUFile *f, void *s)
+{
+ int i;
+
+ for (i = 0; i < (sizeof vapic_bios) / 4; ++i)
+ qemu_put_be32s(f, &((uint32_t *)&vapic_bios)[i]);
+ qemu_put_be32s(f, &bios_enabled);
+ qemu_put_be32s(f, &real_tpr);
+ qemu_put_be32s(f, &bios_addr);
+ qemu_put_be32s(f, &vapic_phys);
+ qemu_put_be32s(f, &vbios_desc_phys);
+}
+
+static int tpr_load(QEMUFile *f, void *s, int version_id)
+{
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ for (i = 0; i < (sizeof vapic_bios) / 4; ++i)
+ qemu_get_be32s(f, &((uint32_t *)&vapic_bios)[i]);
+ qemu_get_be32s(f, &bios_enabled);
+ qemu_get_be32s(f, &real_tpr);
+ qemu_get_be32s(f, &bios_addr);
+ qemu_get_be32s(f, &vapic_phys);
+ qemu_get_be32s(f, &vbios_desc_phys);
+
+ if (bios_enabled) {
+ CPUState *env = first_cpu->next_cpu;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ enable_vapic(env);
+ }
+
+ return 0;
+}
+
+static void vtpr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ CPUState *env = cpu_single_env;
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ uint32_t rip;
+
+ kvm_get_regs(kvm_context, env->cpu_index, &regs);
+ rip = regs.rip - 2;
+ write_byte_virt(env, rip, 0x66);
+ write_byte_virt(env, rip + 1, 0x90);
+ if (bios_enabled)
+ return;
+ if (!bios_is_mapped(env, rip))
+ printf("bios not mapped?\n");
+ kvm_get_sregs(kvm_context, env->cpu_index, &sregs);
+ for (addr = 0xfffff000u; addr >= 0x80000000u; addr -= 4096)
+ if (map_addr(&sregs, addr, NULL) == 0xfee00000u) {
+ real_tpr = addr + 0x80;
+ break;
+ }
+ bios_enabled = 1;
+ update_vbios_real_tpr();
+ enable_vapic(env);
+}
+
+void kvm_tpr_opt_setup(CPUState *env)
+{
+ register_savevm("kvm-tpr-opt", 0, 1, tpr_save, tpr_load, NULL);
+ register_ioport_write(0x7e, 1, 1, vtpr_ioport_write, NULL);
+}
+
diff --git a/kvm.h b/kvm.h
index efce1450d..eeed3dcaa 100644
--- a/kvm.h
+++ b/kvm.h
@@ -14,6 +14,8 @@
#ifndef QEMU_KVM_H
#define QEMU_KVM_H
+#ifdef KVM_UPSTREAM
+
#include "config.h"
#ifdef CONFIG_KVM
@@ -77,3 +79,5 @@ int kvm_arch_init(KVMState *s, int smp_cpus);
int kvm_arch_init_vcpu(CPUState *env);
#endif
+
+#endif
diff --git a/kvm/.gitignore b/kvm/.gitignore
new file mode 100644
index 000000000..fcdc35789
--- /dev/null
+++ b/kvm/.gitignore
@@ -0,0 +1,62 @@
+*.o
+*.d
+*~
+*.flat
+*.a
+config.mak
+.*.cmd
+qemu/config-host.h
+qemu/config-host.mak
+user/test/bootstrap
+user/kvmctl
+qemu/dyngen
+qemu/x86_64-softmmu
+qemu/qemu-img
+qemu/qemu-nbd
+*.ko
+*.mod.c
+bios/*.bin
+bios/*.sym
+bios/*.txt
+bios/acpi-dsdt.aml
+vgabios/*.bin
+vgabios/*.txt
+extboot/extboot.bin
+extboot/extboot.img
+extboot/signrom
+kernel/config.kbuild
+kernel/modules.order
+kernel/Module.symvers
+kernel/Modules.symvers
+kernel/Module.markers
+kernel/.tmp_versions
+kernel/include-compat/asm
+kernel/include-compat/asm-x86/asm-x86
+kernel/include
+kernel/x86/modules.order
+kernel/x86/i825[49].[ch]
+kernel/x86/kvm_main.c
+kernel/x86/kvm_svm.h
+kernel/x86/vmx.[ch]
+kernel/x86/svm.[ch]
+kernel/x86/mmu.[ch]
+kernel/x86/paging_tmpl.h
+kernel/x86/x86_emulate.[ch]
+kernel/x86/ioapic.[ch]
+kernel/x86/iodev.h
+kernel/x86/irq.[ch]
+kernel/x86/kvm_trace.c
+kernel/x86/lapic.[ch]
+kernel/x86/tss.h
+kernel/x86/x86.[ch]
+kernel/x86/coalesced_mmio.[ch]
+kernel/x86/kvm_cache_regs.h
+kernel/x86/vtd.c
+kernel/x86/irq_comm.c
+qemu/pc-bios/extboot.bin
+qemu/qemu-doc.html
+qemu/*.[18]
+qemu/*.pod
+qemu/qemu-tech.html
+user/kvmtrace
+user/test/x86/bootstrap
diff --git a/kvm/Makefile b/kvm/Makefile
new file mode 100644
index 000000000..a7ce0ba13
--- /dev/null
+++ b/kvm/Makefile
@@ -0,0 +1,124 @@
+
+include config.mak
+
+DESTDIR=
+
+rpmrelease = devel
+
+sane-arch = $(subst i386,x86,$(subst x86_64,x86,$(subst s390x,s390,$(ARCH))))
+
+.PHONY: kernel user libkvm qemu bios vgabios extboot clean libfdt cscope
+
+all: libkvm qemu
+ifneq '$(filter $(ARCH), x86_64 i386 ia64)' ''
+ all: $(if $(WANT_MODULE), kernel) user
+endif
+
+kcmd = $(if $(WANT_MODULE),,@\#)
+
+qemu kernel user libkvm:
+ $(MAKE) -C $@
+
+qemu: libkvm
+ifneq '$(filter $(ARCH), i386 x86_64)' ''
+ qemu: extboot
+endif
+ifneq '$(filter $(ARCH), powerpc ia64)' ''
+ qemu: libfdt
+endif
+user: libkvm
+
+user libkvm qemu: header-sync-$(if $(WANT_MODULE),n,y)
+
+header-sync-n:
+
+header-sync-y:
+ make -C kernel \
+ LINUX=$(if $(KERNELSOURCEDIR),$(KERNELSOURCEDIR),$(KERNELDIR)) \
+ header-sync
+ rm -f kernel/include/asm
+ ln -sf asm-$(sane-arch) kernel/include/asm
+
+bios:
+ $(MAKE) -C $@
+ cp bios/BIOS-bochs-latest qemu/pc-bios/bios.bin
+
+vgabios:
+ $(MAKE) -C $@
+ cp vgabios/VGABIOS-lgpl-latest.bin qemu/pc-bios/vgabios.bin
+ cp vgabios/VGABIOS-lgpl-latest.cirrus.bin qemu/pc-bios/vgabios-cirrus.bin
+
+extboot:
+ $(MAKE) -C $@
+ if ! [ -f qemu/pc-bios/extboot.bin ] \
+ || ! cmp -s qemu/pc-bios/extboot.bin extboot/extboot.bin; then \
+ cp extboot/extboot.bin qemu/pc-bios/extboot.bin; \
+ fi
+libfdt:
+ $(MAKE) -C $@
+
+LINUX=linux-2.6
+
+sync:
+ make -C kernel sync LINUX=$(shell readlink -f "$(LINUX)")
+
+bindir = /usr/bin
+bin = $(bindir)/kvm
+initdir = /etc/init.d
+confdir = /etc/kvm
+utilsdir = /etc/kvm/utils
+
+install-rpm:
+ mkdir -p $(DESTDIR)/$(bindir)
+ mkdir -p $(DESTDIR)/$(confdir)
+ mkdir -p $(DESTDIR)/$(initdir)
+ mkdir -p $(DESTDIR)/$(utilsdir)
+ mkdir -p $(DESTDIR)/etc/udev/rules.d
+ make -C qemu DESTDIR=$(DESTDIR)/ install
+ ln -sf /usr/kvm/bin/qemu-system-x86_64 $(DESTDIR)/$(bin)
+ install -m 755 kvm_stat $(DESTDIR)/$(bindir)/kvm_stat
+ cp scripts/kvm $(DESTDIR)/$(initdir)/kvm
+ cp scripts/qemu-ifup $(DESTDIR)/$(confdir)/qemu-ifup
+ install -t $(DESTDIR)/etc/udev/rules.d scripts/*kvm*.rules
+
+install:
+ $(kcmd)make -C kernel DESTDIR="$(DESTDIR)" install
+ make -C libkvm DESTDIR="$(DESTDIR)" install
+ make -C qemu DESTDIR="$(DESTDIR)" install
+
+tmpspec = .tmp.kvm.spec
+RPMTOPDIR = $$(pwd)/rpmtop
+
+rpm: srpm
+ rm -rf $(RPMTOPDIR)/BUILD
+ mkdir -p $(RPMTOPDIR)/{BUILD,RPMS/$$(uname -i)}
+ rpmbuild --rebuild \
+ --define="_topdir $(RPMTOPDIR)" \
+ $(RPMTOPDIR)/SRPMS/kvm-0.0-$(rpmrelease).src.rpm
+
+srpm:
+ mkdir -p $(RPMTOPDIR)/{SOURCES,SRPMS}
+ sed 's/^Release:.*/Release: $(rpmrelease)/' kvm.spec > $(tmpspec)
+ tar czf $(RPMTOPDIR)/SOURCES/kvm.tar.gz qemu
+ tar czf $(RPMTOPDIR)/SOURCES/user.tar.gz user
+ tar czf $(RPMTOPDIR)/SOURCES/libkvm.tar.gz libkvm
+ tar czf $(RPMTOPDIR)/SOURCES/kernel.tar.gz kernel
+ tar czf $(RPMTOPDIR)/SOURCES/scripts.tar.gz scripts
+ tar czf $(RPMTOPDIR)/SOURCES/extboot.tar.gz extboot
+ cp Makefile configure kvm_stat $(RPMTOPDIR)/SOURCES
+ rpmbuild --define="_topdir $(RPMTOPDIR)" -bs $(tmpspec)
+ $(RM) $(tmpspec)
+
+clean:
+ for i in $(if $(WANT_MODULE), kernel) user libkvm qemu libfdt; do \
+ make -C $$i clean; \
+ done
+ rm -f ./cscope.*
+
+distclean: clean
+ rm -f config.mak user/config.mak
+
+cscope:
+ rm -f ./cscope.*
+ find . -wholename './kernel' -prune -o -name "*.[ch]" -print > ./cscope.files
+ cscope -b
diff --git a/kvm/bios/.cvsignore b/kvm/bios/.cvsignore
new file mode 100644
index 000000000..f3c7a7c5d
--- /dev/null
+++ b/kvm/bios/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/kvm/bios/Makefile b/kvm/bios/Makefile
new file mode 100644
index 000000000..2d1f40d71
--- /dev/null
+++ b/kvm/bios/Makefile
@@ -0,0 +1,130 @@
+# Copyright (C) 2001 MandrakeSoft S.A.
+#
+# MandrakeSoft S.A.
+# 43, rue d'Aboukir
+# 75002 Paris - France
+# http://www.linux-mandrake.com/
+# http://www.mandrakesoft.com/
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Makefile for the BIOS component of bochs
+
+
+.SUFFIXES: .cc
+
+srcdir = .
+
+
+SHELL = /bin/sh
+
+CXX = g++
+CXXFLAGS = -g -O2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES
+
+# cc-option, copied from user/Makefile
+# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
+
+cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+CFLAGS = -m32
+CFLAGS += $(call cc-option, -fno-stack-protector, "")
+CFLAGS += $(call cc-option, -fno-stack-protector-all, "")
+
+LDFLAGS =
+LIBS = -lm
+RANLIB = ranlib
+
+BCC = bcc
+GCC = gcc $(CFLAGS)
+HOST_CC = gcc
+AS86 = as86
+
+BX_INCDIRS = -I.. -I$(srcdir)/.. -I../iodev -I$(srcdir)/../iodev
+LOCAL_CXXFLAGS =
+
+BUILDDATE = `date '+%m/%d/%y'`
+BIOS_BUILD_DATE = "-DBIOS_BUILD_DATE=\"$(BUILDDATE)\""
+#
+# -------- end configurable options --------------------------
+#
+
+
+.cc.o:
+ $(CXX) -c $(BX_INCDIRS) $(CXXFLAGS) $(LOCAL_CXXFLAGS) $< -o $@
+
+
+bios: biossums BIOS-bochs-latest BIOS-bochs-legacy
+
+clean:
+ rm -f *.o *.a *.s _rombios*_.c rombios*.txt rombios*.sym
+ rm -f usage biossums rombios16.bin
+ rm -f rombios32.bin rombios32.out acpi-dsdt.hex
+
+dist-clean: clean
+ rm -f Makefile
+
+bios-clean:
+ rm -f BIOS-bochs-*
+
+BIOS-bochs-legacy: rombios.c apmbios.S biossums rombios.h
+ $(GCC) $(BIOS_BUILD_DATE) -DLEGACY -E -P $< > _rombiosl_.c
+ $(BCC) -o rombiosl.s -C-c -D__i86__ -0 -S _rombiosl_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' rombiosl.s > _rombiosl_.s
+ $(AS86) _rombiosl_.s -b tmpl.bin -u- -w- -g -0 -j -O -l rombiosl.txt
+ -perl ${srcdir}/makesym.perl < rombiosl.txt > rombiosl.sym
+ mv tmpl.bin $@
+ ./biossums $@
+ rm -f _rombiosl_.s
+
+
+rombios16.bin: rombios.c apmbios.S biossums rombios.h
+ $(GCC) $(BIOS_BUILD_DATE) -E -P $< > _rombios_.c
+ $(BCC) -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s
+ $(AS86) _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt
+ -perl ${srcdir}/makesym.perl < rombios.txt > rombios.sym
+ mv tmp.bin rombios16.bin
+ ./biossums rombios16.bin
+ rm -f _rombios_.s
+
+
+rombios32.bin: rombios32.out rombios.h
+ objcopy -O binary $< $@
+ ./biossums -pad $@
+
+rombios32.out: rombios32start.o rombios32.o vapic.o rombios32.ld
+ ld -o $@ -T rombios32.ld rombios32start.o vapic.o rombios32.o
+
+rombios32.o: rombios32.c acpi-dsdt.hex
+ $(GCC) -m32 -O2 -Wall -c -o $@ $<
+
+acpi-dsdt.hex: acpi-dsdt.dsl
+ cpp -P $< $<.i
+ iasl -tc -p $@ $<.i
+ sed -i -e's/^unsigned/const unsigned/' $@
+ rm $<.i
+
+rombios32start.o: rombios32start.S
+ $(GCC) -m32 -c -o $@ $<
+
+vapic.o: vapic.S
+ $(GCC) -m32 -c -o $@ $<
+
+BIOS-bochs-latest: rombios16.bin rombios32.bin
+ cat rombios32.bin rombios16.bin > $@
+
+biossums: biossums.c
+ $(HOST_CC) -o biossums biossums.c
diff --git a/kvm/bios/Makefile.in b/kvm/bios/Makefile.in
new file mode 100644
index 000000000..3d3d195f2
--- /dev/null
+++ b/kvm/bios/Makefile.in
@@ -0,0 +1,118 @@
+# 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
+GCC32 = gcc -m32 -fno-stack-protector
+AS86 = as86
+
+BX_INCDIRS = -I.. -I$(srcdir)/.. -I../iodev -I$(srcdir)/../iodev
+LOCAL_CXXFLAGS =
+
+BUILDDATE = `date '+%m/%d/%y'`
+BIOS_BUILD_DATE = "-DBIOS_BUILD_DATE=\"$(BUILDDATE)\""
+#
+# -------- end configurable options --------------------------
+#
+
+
+.@CPP_SUFFIX@.o:
+ $(CXX) -c $(BX_INCDIRS) $(CXXFLAGS) $(LOCAL_CXXFLAGS) @CXXFP@$< @OFP@$@
+
+
+bios: biossums BIOS-bochs-latest BIOS-bochs-legacy
+
+clean:
+ @RMCOMMAND@ *.o *.a *.s _rombios*_.c rombios*.txt rombios*.sym
+ @RMCOMMAND@ usage biossums rombios16.bin
+ @RMCOMMAND@ rombios32.bin rombios32.out
+
+dist-clean: clean
+ @RMCOMMAND@ Makefile
+
+bios-clean:
+ @RMCOMMAND@ BIOS-bochs-*
+
+BIOS-bochs-legacy: rombios.c apmbios.S biossums rombios.h
+ $(GCC32) $(BIOS_BUILD_DATE) -DLEGACY -E -P $< > _rombiosl_.c
+ $(BCC) -o rombiosl.s -C-c -D__i86__ -0 -S _rombiosl_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' rombiosl.s > _rombiosl_.s
+ $(AS86) _rombiosl_.s -b tmpl.bin -u- -w- -g -0 -j -O -l rombiosl.txt
+ -perl ${srcdir}/makesym.perl < rombiosl.txt > rombiosl.sym
+ mv tmpl.bin $@
+ ./biossums $@
+ @RMCOMMAND@ _rombiosl_.s
+
+
+rombios16.bin: rombios.c apmbios.S biossums rombios.h
+ $(GCC32) $(BIOS_BUILD_DATE) -E -P $< > _rombios_.c
+ $(BCC) -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s
+ $(AS86) _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt
+ -perl ${srcdir}/makesym.perl < rombios.txt > rombios.sym
+ mv tmp.bin rombios16.bin
+ ./biossums rombios16.bin
+ @RMCOMMAND@ _rombios_.s
+
+
+rombios32.bin: rombios32.out rombios.h
+ objcopy -O binary $< $@
+ ./biossums -pad $@
+
+rombios32.out: rombios32start.o rombios32.o rombios32.ld
+ ld -o $@ -T $(srcdir)/rombios32.ld rombios32start.o rombios32.o
+
+rombios32.o: rombios32.c acpi-dsdt.hex
+ $(GCC32) -O2 -Wall -c -o $@ $<
+
+ifeq ("1", "0")
+acpi-dsdt.hex: acpi-dsdt.dsl
+ iasl -tc -p $@ $<
+ sed -i -e's/^unsigned/const unsigned/' $@
+endif
+
+rombios32start.o: rombios32start.S
+ $(GCC32) -c -o $@ $<
+
+BIOS-bochs-latest: rombios16.bin rombios32.bin
+ cat rombios32.bin rombios16.bin > $@
+
+biossums: biossums.o
diff --git a/kvm/bios/acpi-dsdt.dsl b/kvm/bios/acpi-dsdt.dsl
new file mode 100755
index 000000000..d67616d63
--- /dev/null
+++ b/kvm/bios/acpi-dsdt.dsl
@@ -0,0 +1,785 @@
+/*
+ * 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)
+ {
+ OperationRegion( PRST, SystemIO, 0xaf00, 0x02)
+ Field (PRST, ByteAcc, NoLock, WriteAsZeros)
+ {
+ PRU, 8,
+ PRD, 8,
+ }
+
+#define gen_processor(nr, name) \
+ Processor (CPU##name, nr, 0x0000b010, 0x06) { \
+ Name (TMP, Buffer(0x8) {0x0, 0x8, nr, nr, 0x1, 0x0, 0x0, 0x0}) \
+ Method(_MAT, 0) { \
+ If (And(\_PR.PRU, ShiftLeft(1, nr))) { Return(TMP) } \
+ Else { Return(0x0) } \
+ } \
+ Method (_STA) { \
+ Return(0xF) \
+ } \
+ } \
+
+
+
+ Processor (CPU0, 0x00, 0x0000b010, 0x06) {Method (_STA) { Return(0xF)}}
+ gen_processor(1, 1)
+ gen_processor(2, 2)
+ gen_processor(3, 3)
+ gen_processor(4, 4)
+ gen_processor(5, 5)
+ gen_processor(6, 6)
+ gen_processor(7, 7)
+ gen_processor(8, 8)
+ gen_processor(9, 9)
+ gen_processor(10, A)
+ gen_processor(11, B)
+ gen_processor(12, C)
+ gen_processor(13, D)
+ gen_processor(14, E)
+ }
+
+ Scope (\)
+ {
+ /* Debug Output */
+ OperationRegion (DBG, SystemIO, 0xb044, 0x04)
+ Field (DBG, DWordAcc, NoLock, Preserve)
+ {
+ DBGL, 32,
+ }
+ }
+
+
+ /* PCI Bus definition */
+ Scope(\_SB) {
+ Device(PCI0) {
+ Name (_HID, EisaId ("PNP0A03"))
+ Name (_ADR, 0x00)
+ Name (_UID, 1)
+ Name(_PRT, Package() {
+ /* PCI IRQ routing table, example from ACPI 2.0a specification,
+ section 6.2.8.1 */
+ /* Note: we provide the same info as the PCI routing
+ table of the Bochs BIOS */
+
+#define prt_slot(nr, lnk0, lnk1, lnk2, lnk3) \
+ Package() { nr##ffff, 0, lnk0, 0 }, \
+ Package() { nr##ffff, 1, lnk1, 0 }, \
+ Package() { nr##ffff, 2, lnk2, 0 }, \
+ Package() { nr##ffff, 3, lnk3, 0 }
+
+#define prt_slot0(nr) prt_slot(nr, LNKD, LNKA, LNKB, LNKC)
+#define prt_slot1(nr) prt_slot(nr, LNKA, LNKB, LNKC, LNKD)
+#define prt_slot2(nr) prt_slot(nr, LNKB, LNKC, LNKD, LNKA)
+#define prt_slot3(nr) prt_slot(nr, LNKC, LNKD, LNKA, LNKB)
+
+ prt_slot0(0x0000),
+ prt_slot1(0x0001),
+ prt_slot2(0x0002),
+ prt_slot3(0x0003),
+ prt_slot0(0x0004),
+ prt_slot1(0x0005),
+ prt_slot2(0x0006),
+ prt_slot3(0x0007),
+ prt_slot0(0x0008),
+ prt_slot1(0x0009),
+ prt_slot2(0x000a),
+ prt_slot3(0x000b),
+ prt_slot0(0x000c),
+ prt_slot1(0x000d),
+ prt_slot2(0x000e),
+ prt_slot3(0x000f),
+ prt_slot0(0x0010),
+ prt_slot1(0x0011),
+ prt_slot2(0x0012),
+ prt_slot3(0x0013),
+ prt_slot0(0x0014),
+ prt_slot1(0x0015),
+ prt_slot2(0x0016),
+ prt_slot3(0x0017),
+ prt_slot0(0x0018),
+ prt_slot1(0x0019),
+ prt_slot2(0x001a),
+ prt_slot3(0x001b),
+ prt_slot0(0x001c),
+ prt_slot1(0x001d),
+ prt_slot2(0x001e),
+ prt_slot3(0x001f),
+ })
+
+ OperationRegion(PCST, SystemIO, 0xae00, 0x08)
+ Field (PCST, DWordAcc, NoLock, WriteAsZeros)
+ {
+ PCIU, 32,
+ PCID, 32,
+ }
+
+ OperationRegion(SEJ, SystemIO, 0xae08, 0x04)
+ Field (SEJ, DWordAcc, NoLock, WriteAsZeros)
+ {
+ B0EJ, 32,
+ }
+
+#define hotplug_slot(name, nr) \
+ Device (S##name) { \
+ Name (_ADR, nr##0000) \
+ Method (_EJ0,1) { \
+ Store(ShiftLeft(1, nr), B0EJ) \
+ Return (0x0) \
+ } \
+ Name (_SUN, name) \
+ }
+
+ hotplug_slot(1, 0x0001)
+ hotplug_slot(2, 0x0002)
+ hotplug_slot(3, 0x0003)
+ hotplug_slot(4, 0x0004)
+ hotplug_slot(5, 0x0005)
+ hotplug_slot(6, 0x0006)
+ hotplug_slot(7, 0x0007)
+ hotplug_slot(8, 0x0008)
+ hotplug_slot(9, 0x0009)
+ hotplug_slot(10, 0x000a)
+ hotplug_slot(11, 0x000b)
+ hotplug_slot(12, 0x000c)
+ hotplug_slot(13, 0x000d)
+ hotplug_slot(14, 0x000e)
+ hotplug_slot(15, 0x000f)
+ hotplug_slot(16, 0x0010)
+ hotplug_slot(17, 0x0011)
+ hotplug_slot(18, 0x0012)
+ hotplug_slot(19, 0x0013)
+ hotplug_slot(20, 0x0014)
+ hotplug_slot(21, 0x0015)
+ hotplug_slot(22, 0x0016)
+ hotplug_slot(23, 0x0017)
+ hotplug_slot(24, 0x0018)
+ hotplug_slot(25, 0x0019)
+ hotplug_slot(26, 0x001a)
+ hotplug_slot(27, 0x001b)
+ hotplug_slot(28, 0x001c)
+ hotplug_slot(29, 0x001d)
+ hotplug_slot(30, 0x001e)
+ hotplug_slot(31, 0x001f)
+
+ Name (_CRS, ResourceTemplate ()
+ {
+ WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
+ 0x0000, // Address Space Granularity
+ 0x0000, // Address Range Minimum
+ 0x00FF, // Address Range Maximum
+ 0x0000, // Address Translation Offset
+ 0x0100, // Address Length
+ ,, )
+ IO (Decode16,
+ 0x0CF8, // Address Range Minimum
+ 0x0CF8, // Address Range Maximum
+ 0x01, // Address Alignment
+ 0x08, // Address Length
+ )
+ WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+ 0x0000, // Address Space Granularity
+ 0x0000, // Address Range Minimum
+ 0x0CF7, // Address Range Maximum
+ 0x0000, // Address Translation Offset
+ 0x0CF8, // Address Length
+ ,, , TypeStatic)
+ WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
+ 0x0000, // Address Space Granularity
+ 0x0D00, // Address Range Minimum
+ 0xFFFF, // Address Range Maximum
+ 0x0000, // Address Translation Offset
+ 0xF300, // Address Length
+ ,, , TypeStatic)
+ DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
+ 0x00000000, // Address Space Granularity
+ 0x000A0000, // Address Range Minimum
+ 0x000BFFFF, // Address Range Maximum
+ 0x00000000, // Address Translation Offset
+ 0x00020000, // Address Length
+ ,, , AddressRangeMemory, TypeStatic)
+ DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,
+ 0x00000000, // Address Space Granularity
+ 0xE0000000, // Address Range Minimum
+ 0xFEBFFFFF, // Address Range Maximum
+ 0x00000000, // Address Translation Offset
+ 0x1EC00000, // Address Length
+ ,, , AddressRangeMemory, TypeStatic)
+ })
+ }
+ }
+
+ Scope(\_SB.PCI0) {
+ Device (VGA) {
+ Name (_ADR, 0x00020000)
+ Method (_S1D, 0, NotSerialized)
+ {
+ Return (0x00)
+ }
+ Method (_S2D, 0, NotSerialized)
+ {
+ Return (0x00)
+ }
+ Method (_S3D, 0, NotSerialized)
+ {
+ Return (0x00)
+ }
+ }
+
+ /* PIIX3 ISA bridge */
+ Device (ISA) {
+ Name (_ADR, 0x00010000)
+
+ /* PIIX PCI to ISA irq remapping */
+ OperationRegion (P40C, PCI_Config, 0x60, 0x04)
+
+ /* Real-time clock */
+ Device (RTC)
+ {
+ Name (_HID, EisaId ("PNP0B00"))
+ Name (_CRS, ResourceTemplate ()
+ {
+ IO (Decode16, 0x0070, 0x0070, 0x10, 0x02)
+ IRQNoFlags () {8}
+ IO (Decode16, 0x0072, 0x0072, 0x02, 0x06)
+ })
+ }
+
+ /* Keyboard seems to be important for WinXP install */
+ Device (KBD)
+ {
+ Name (_HID, EisaId ("PNP0303"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0f)
+ }
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (TMP, ResourceTemplate ()
+ {
+ IO (Decode16,
+ 0x0060, // Address Range Minimum
+ 0x0060, // Address Range Maximum
+ 0x01, // Address Alignment
+ 0x01, // Address Length
+ )
+ IO (Decode16,
+ 0x0064, // Address Range Minimum
+ 0x0064, // Address Range Maximum
+ 0x01, // Address Alignment
+ 0x01, // Address Length
+ )
+ IRQNoFlags ()
+ {1}
+ })
+ Return (TMP)
+ }
+ }
+
+ /* PS/2 mouse */
+ Device (MOU)
+ {
+ Name (_HID, EisaId ("PNP0F13"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0f)
+ }
+
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (TMP, ResourceTemplate ()
+ {
+ IRQNoFlags () {12}
+ })
+ Return (TMP)
+ }
+ }
+
+ /* PS/2 floppy controller */
+ Device (FDC0)
+ {
+ Name (_HID, EisaId ("PNP0700"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0F)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x03F2, 0x03F2, 0x00, 0x04)
+ IO (Decode16, 0x03F7, 0x03F7, 0x00, 0x01)
+ IRQNoFlags () {6}
+ DMA (Compatibility, NotBusMaster, Transfer8) {2}
+ })
+ Return (BUF0)
+ }
+ }
+
+ /* Parallel port */
+ Device (LPT)
+ {
+ Name (_HID, EisaId ("PNP0400"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (\_SB.PCI0.PX13.DRSA, Local0)
+ And (Local0, 0x80000000, Local0)
+ If (LEqual (Local0, 0))
+ {
+ Return (0x00)
+ }
+ Else
+ {
+ Return (0x0F)
+ }
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x0378, 0x0378, 0x08, 0x08)
+ IRQNoFlags () {7}
+ })
+ Return (BUF0)
+ }
+ }
+
+ /* Serial Ports */
+ Device (COM1)
+ {
+ Name (_HID, EisaId ("PNP0501"))
+ Name (_UID, 0x01)
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (\_SB.PCI0.PX13.DRSC, Local0)
+ And (Local0, 0x08000000, Local0)
+ If (LEqual (Local0, 0))
+ {
+ Return (0x00)
+ }
+ Else
+ {
+ Return (0x0F)
+ }
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x03F8, 0x03F8, 0x00, 0x08)
+ IRQNoFlags () {4}
+ })
+ Return (BUF0)
+ }
+ }
+
+ Device (COM2)
+ {
+ Name (_HID, EisaId ("PNP0501"))
+ Name (_UID, 0x02)
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (\_SB.PCI0.PX13.DRSC, Local0)
+ And (Local0, 0x80000000, Local0)
+ If (LEqual (Local0, 0))
+ {
+ Return (0x00)
+ }
+ Else
+ {
+ Return (0x0F)
+ }
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (BUF0, ResourceTemplate ()
+ {
+ IO (Decode16, 0x02F8, 0x02F8, 0x00, 0x08)
+ IRQNoFlags () {3}
+ })
+ Return (BUF0)
+ }
+ }
+ }
+
+ /* PIIX4 PM */
+ Device (PX13) {
+ Name (_ADR, 0x00010003)
+
+ OperationRegion (P13C, PCI_Config, 0x5c, 0x24)
+ Field (P13C, DWordAcc, NoLock, Preserve)
+ {
+ DRSA, 32,
+ DRSB, 32,
+ DRSC, 32,
+ DRSE, 32,
+ DRSF, 32,
+ DRSG, 32,
+ DRSH, 32,
+ DRSI, 32,
+ DRSJ, 32
+ }
+ }
+ }
+
+ /* PCI IRQs */
+ Scope(\_SB) {
+ Field (\_SB.PCI0.ISA.P40C, ByteAcc, NoLock, Preserve)
+ {
+ PRQ0, 8,
+ PRQ1, 8,
+ PRQ2, 8,
+ PRQ3, 8
+ }
+
+ Device(LNKA){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 1)
+ Name(_PRS, ResourceTemplate(){
+ Interrupt (, Level, ActiveHigh, Shared)
+ { 5, 10, 11 }
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ0, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ0, 0x80, PRQ0)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ Interrupt (, Level, ActiveHigh, Shared)
+ {1}
+ })
+ CreateDWordField (PRR0, 0x05, TMP)
+ Store (PRQ0, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ Store (Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateDWordField (Arg0, 0x05, TMP)
+ Store (TMP, PRQ0)
+ }
+ }
+ Device(LNKB){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 2)
+ Name(_PRS, ResourceTemplate(){
+ Interrupt (, Level, ActiveHigh, Shared)
+ { 5, 10, 11 }
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ1, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ1, 0x80, PRQ1)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ Interrupt (, Level, ActiveHigh, Shared)
+ {1}
+ })
+ CreateDWordField (PRR0, 0x05, TMP)
+ Store (PRQ1, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ Store (Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateDWordField (Arg0, 0x05, TMP)
+ Store (TMP, PRQ1)
+ }
+ }
+ Device(LNKC){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 3)
+ Name(_PRS, ResourceTemplate(){
+ Interrupt (, Level, ActiveHigh, Shared)
+ { 5, 10, 11 }
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ2, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ2, 0x80, PRQ2)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ Interrupt (, Level, ActiveHigh, Shared)
+ {1}
+ })
+ CreateDWordField (PRR0, 0x05, TMP)
+ Store (PRQ2, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ Store (Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateDWordField (Arg0, 0x05, TMP)
+ Store (TMP, PRQ2)
+ }
+ }
+ Device(LNKD){
+ Name(_HID, EISAID("PNP0C0F")) // PCI interrupt link
+ Name(_UID, 4)
+ Name(_PRS, ResourceTemplate(){
+ Interrupt (, Level, ActiveHigh, Shared)
+ { 5, 10, 11 }
+ })
+ Method (_STA, 0, NotSerialized)
+ {
+ Store (0x0B, Local0)
+ If (And (0x80, PRQ3, Local1))
+ {
+ Store (0x09, Local0)
+ }
+ Return (Local0)
+ }
+ Method (_DIS, 0, NotSerialized)
+ {
+ Or (PRQ3, 0x80, PRQ3)
+ }
+ Method (_CRS, 0, NotSerialized)
+ {
+ Name (PRR0, ResourceTemplate ()
+ {
+ Interrupt (, Level, ActiveHigh, Shared)
+ {1}
+ })
+ CreateDWordField (PRR0, 0x05, TMP)
+ Store (PRQ3, Local0)
+ If (LLess (Local0, 0x80))
+ {
+ Store (Local0, TMP)
+ }
+ Else
+ {
+ Store (Zero, TMP)
+ }
+ Return (PRR0)
+ }
+ Method (_SRS, 1, NotSerialized)
+ {
+ CreateDWordField (Arg0, 0x05, TMP)
+ Store (TMP, PRQ3)
+ }
+ }
+ }
+
+ /*
+ * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes:
+ * must match piix4 emulation.
+ */
+ Name (\_S3, Package (0x04)
+ {
+ 0x01, /* PM1a_CNT.SLP_TYP */
+ 0x01, /* PM1b_CNT.SLP_TYP */
+ Zero, /* reserved */
+ Zero /* reserved */
+ })
+ Name (\_S4, Package (0x04)
+ {
+ Zero, /* PM1a_CNT.SLP_TYP */
+ Zero, /* PM1b_CNT.SLP_TYP */
+ Zero, /* reserved */
+ Zero /* reserved */
+ })
+ Name (\_S5, Package (0x04)
+ {
+ Zero, /* PM1a_CNT.SLP_TYP */
+ Zero, /* PM1b_CNT.SLP_TYP */
+ Zero, /* reserved */
+ Zero /* reserved */
+ })
+ Scope (\_GPE)
+ {
+
+#define gen_cpu_hotplug(name, nr) \
+ If (And(\_PR.PRU, ShiftLeft(1, nr))) { \
+ Notify(\_PR.CPU##name, 1) \
+ } \
+ If (And(\_PR.PRD, ShiftLeft(1, nr))) { \
+ Notify(\_PR.CPU##name, 3) \
+ }
+
+ Method(_L00) {
+ gen_cpu_hotplug(1, 1)
+ gen_cpu_hotplug(2, 2)
+ gen_cpu_hotplug(3, 3)
+ gen_cpu_hotplug(4, 4)
+ gen_cpu_hotplug(5, 5)
+ gen_cpu_hotplug(6, 6)
+ gen_cpu_hotplug(7, 7)
+ gen_cpu_hotplug(8, 8)
+ gen_cpu_hotplug(9, 9)
+ gen_cpu_hotplug(A, 10)
+ gen_cpu_hotplug(B, 11)
+ gen_cpu_hotplug(C, 12)
+ gen_cpu_hotplug(D, 13)
+ gen_cpu_hotplug(E, 14)
+
+ Return(0x01)
+ }
+
+#define gen_pci_hotplug(nr) \
+ If (And(\_SB.PCI0.PCIU, ShiftLeft(1, nr))) { \
+ Notify(\_SB.PCI0.S##nr, 1) \
+ } \
+ If (And(\_SB.PCI0.PCID, ShiftLeft(1, nr))) { \
+ Notify(\_SB.PCI0.S##nr, 3) \
+ }
+
+ Method(_L01) {
+ gen_pci_hotplug(1)
+ gen_pci_hotplug(2)
+ gen_pci_hotplug(3)
+ gen_pci_hotplug(4)
+ gen_pci_hotplug(5)
+ gen_pci_hotplug(6)
+ gen_pci_hotplug(7)
+ gen_pci_hotplug(8)
+ gen_pci_hotplug(9)
+ gen_pci_hotplug(10)
+ gen_pci_hotplug(11)
+ gen_pci_hotplug(12)
+ gen_pci_hotplug(13)
+ gen_pci_hotplug(14)
+ gen_pci_hotplug(15)
+ gen_pci_hotplug(16)
+ gen_pci_hotplug(17)
+ gen_pci_hotplug(18)
+ gen_pci_hotplug(19)
+ gen_pci_hotplug(20)
+ gen_pci_hotplug(21)
+ gen_pci_hotplug(22)
+ gen_pci_hotplug(23)
+ gen_pci_hotplug(24)
+ gen_pci_hotplug(25)
+ gen_pci_hotplug(26)
+ gen_pci_hotplug(27)
+ gen_pci_hotplug(28)
+ gen_pci_hotplug(29)
+ gen_pci_hotplug(30)
+ gen_pci_hotplug(31)
+
+ Return(0x01)
+ }
+ Method(_L02) {
+ Return(0x01)
+ }
+ Method(_L03) {
+ Return(0x01)
+ }
+ Method(_L04) {
+ Return(0x01)
+ }
+ Method(_L05) {
+ Return(0x01)
+ }
+ Method(_L06) {
+ Return(0x01)
+ }
+ Method(_L07) {
+ Return(0x01)
+ }
+ Method(_L08) {
+ Return(0x01)
+ }
+ Method(_L09) {
+ Return(0x01)
+ }
+ Method(_L0A) {
+ Return(0x01)
+ }
+ Method(_L0B) {
+ Return(0x01)
+ }
+ Method(_L0C) {
+ Return(0x01)
+ }
+ Method(_L0D) {
+ Return(0x01)
+ }
+ Method(_L0E) {
+ Return(0x01)
+ }
+ Method(_L0F) {
+ Return(0x01)
+ }
+ }
+}
diff --git a/kvm/bios/apmbios.S b/kvm/bios/apmbios.S
new file mode 100644
index 000000000..cf0fad033
--- /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..631265404
--- /dev/null
+++ b/kvm/bios/makesym.perl
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+#
+# $Id: makesym.perl,v 1.1 2002/11/24 22:45:40 bdenney Exp $
+#
+# Read output file from as86 (e.g. rombios.txt) and write out a symbol
+# table suitable for the Bochs debugger.
+#
+
+$WHERE_BEFORE_SYM_TABLE = 0;
+$WHERE_IN_SYM_TABLE = 1;
+$WHERE_AFTER_SYM_TABLE = 2;
+
+$where = $WHERE_BEFORE_SYM_TABLE;
+while (<STDIN>) {
+ chop;
+ if ($where == WHERE_BEFORE_SYM_TABLE && /^Symbols:/) {
+ $where = $WHERE_IN_SYM_TABLE;
+ } elsif ($where == $WHERE_IN_SYM_TABLE && /^$/) {
+ $where = $WHERE_AFTER_SYM_TABLE;
+ }
+ if ($where == $WHERE_IN_SYM_TABLE) {
+ @F = split (/\s+/);
+ ($name[0], $junk, $addr[0], $junk, $name[1], $junk, $addr[1]) = @F;
+ foreach $col (0,1) {
+ next if length $addr[$col] < 1;
+ $addr[$col] =~ tr/A-Z/a-z/;
+ $addr[$col] = "000f" . $addr[$col];
+ print "$addr[$col] $name[$col]\n";
+ }
+ }
+}
diff --git a/kvm/bios/notes b/kvm/bios/notes
new file mode 100644
index 000000000..ae1073e98
--- /dev/null
+++ b/kvm/bios/notes
@@ -0,0 +1,44 @@
+####################
+# Read Disk Sector #
+####################
+
+System programming:
+-------------------
+
+(Int 13h, ah=2)
+in 1f7 until BSY cleared
+out 1f2, AL # number of sectors
+out 1f3, cl (bits 0-5) # starting sector number
+out 1f4, ch # cylinder number bits 0..7, 0 based
+out 1f5, cl (bits 6,7) & dh (bits 6,7) # cyl, bits 8..9, 10..11
+out 1f6, dh (bits 0..3) --> bits 0..3 # head number
+ dh (bits 4..5) --> ??? # head number
+ dl (bit 0) --> bit 4 # drive number
+out 1f7, 0x20 # read sectors command normal
+
+
+
+Drive response:
+---------------
+
+* drive sets the busy bit in Status Reg to 1
+* if command parameters are wrong:
+ > drive sets the aborted-command bit in the Error register and
+ error bit in the Status register to 1.
+ > Drive also sets the busy bit in the Status register to 0.
+ > Drive then generates an interrupt to the system.
+* else:
+ > drive executes an implied seek to desired track and
+ reads sectors into sector buffer
+ > when sector buffer is filled and the data is ready to be
+ transferred, the drive sets the data-request bit to 1, sets
+ the busy bit to 0, and generates an interrupt.
+ > on a single-sector transfer, after the system has transferred
+ the data, the drive sets the data-request bit and the busy bit to 0.
+ > on a multiple-sector transfer, after the system has transferred
+ the first sector of data, the drive sets the data-request bit to 0,
+ and the busy bit to 1. When each subsequent sector is ready to be
+ transferred, the drive sets the data-request bit to 1, the busy bit to 0,
+ and generates an interrupt. When the system has tranferred the last sector,
+ the drive sets the data-request bit and busy bit to 0.
+
diff --git a/kvm/bios/rombios.c b/kvm/bios/rombios.c
new file mode 100644
index 000000000..b7a240f91
--- /dev/null
+++ b/kvm/bios/rombios.c
@@ -0,0 +1,11450 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rombios.c,v 1.182 2007/08/01 17:09:51 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+// ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
+
+
+// ROM BIOS compatability entry points:
+// ===================================
+// $e05b ; POST Entry Point
+// $e2c3 ; NMI Handler Entry Point
+// $e3fe ; INT 13h Fixed Disk Services Entry Point
+// $e401 ; Fixed Disk Parameter Table
+// $e6f2 ; INT 19h Boot Load Service Entry Point
+// $e6f5 ; Configuration Data Table
+// $e729 ; Baud Rate Generator Table
+// $e739 ; INT 14h Serial Communications Service Entry Point
+// $e82e ; INT 16h Keyboard Service Entry Point
+// $e987 ; INT 09h Keyboard Service Entry Point
+// $ec59 ; INT 13h Diskette Service Entry Point
+// $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
+// $efc7 ; Diskette Controller Parameter Table
+// $efd2 ; INT 17h Printer Service Entry Point
+// $f045 ; INT 10 Functions 0-Fh Entry Point
+// $f065 ; INT 10h Video Support Service Entry Point
+// $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
+// $f841 ; INT 12h Memory Size Service Entry Point
+// $f84d ; INT 11h Equipment List Service Entry Point
+// $f859 ; INT 15h System Services Entry Point
+// $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+// $fe6e ; INT 1Ah Time-of-day Service Entry Point
+// $fea5 ; INT 08h System Timer ISR Entry Point
+// $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
+// $ff53 ; IRET Instruction for Dummy Interrupt Handler
+// $ff54 ; INT 05h Print Screen Service Entry Point
+// $fff0 ; Power-up Entry Point
+// $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
+// $fffe ; System Model ID
+
+// NOTES for ATA/ATAPI driver (cbbochs@free.fr)
+// Features
+// - supports up to 4 ATA interfaces
+// - device/geometry detection
+// - 16bits/32bits device access
+// - pchs/lba access
+// - datain/dataout/packet command support
+//
+// NOTES for El-Torito Boot (cbbochs@free.fr)
+// - CD-ROM booting is only available if ATA/ATAPI Driver is available
+// - Current code is only able to boot mono-session cds
+// - Current code can not boot and emulate a hard-disk
+// the bios will panic otherwise
+// - Current code also use memory in EBDA segement.
+// - I used cmos byte 0x3D to store extended information on boot-device
+// - Code has to be modified modified to handle multiple cdrom drives
+// - Here are the cdrom boot failure codes:
+// 1 : no atapi device found
+// 2 : no atapi cdrom found
+// 3 : can not read cd - BRVD
+// 4 : cd is not eltorito (BRVD)
+// 5 : cd is not eltorito (ISO TAG)
+// 6 : cd is not eltorito (ELTORITO TAG)
+// 7 : can not read cd - boot catalog
+// 8 : boot catalog : bad header
+// 9 : boot catalog : bad platform
+// 10 : boot catalog : bad signature
+// 11 : boot catalog : bootable flag not set
+// 12 : can not read cd - boot image
+//
+// ATA driver
+// - EBDA segment.
+// I used memory starting at 0x121 in the segment
+// - the translation policy is defined in cmos regs 0x39 & 0x3a
+//
+// TODO :
+//
+// int74
+// - needs to be reworked. Uses direct [bp] offsets. (?)
+//
+// int13:
+// - f04 (verify sectors) isn't complete (?)
+// - f02/03/04 should set current cyl,etc in BDA (?)
+// - rewrite int13_relocated & clean up int13 entry code
+//
+// NOTES:
+// - NMI access (bit7 of addr written to 70h)
+//
+// ATA driver
+// - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
+// - could send the multiple-sector read/write commands
+//
+// El-Torito
+// - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
+// - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
+// - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
+// - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
+// This is ok. But DL should be reincremented afterwards.
+// - Fix all "FIXME ElTorito Various"
+// - should be able to boot any cdrom instead of the first one
+//
+// BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
+
+#include "rombios.h"
+
+#define DEBUG_ATA 0
+#define DEBUG_INT13_HD 0
+#define DEBUG_INT13_CD 0
+#define DEBUG_INT13_ET 0
+#define DEBUG_INT13_FL 0
+#define DEBUG_INT15 0
+#define DEBUG_INT16 0
+#define DEBUG_INT1A 0
+#define DEBUG_INT74 0
+#define DEBUG_APM 0
+
+#define BX_CPU 3
+#define BX_USE_PS2_MOUSE 1
+#define BX_CALL_INT15_4F 1
+#define BX_USE_EBDA 1
+#define BX_SUPPORT_FLOPPY 1
+#define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
+#define BX_PCIBIOS 1
+#define BX_APM 1
+
+#define BX_USE_ATADRV 1
+#define BX_ELTORITO_BOOT 1
+
+#define BX_MAX_ATA_INTERFACES 4
+#define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
+
+#define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
+#define BX_DEBUG_SERIAL 0 /* output to COM1 */
+
+ /* model byte 0xFC = AT */
+#define SYS_MODEL_ID 0xFC
+#define SYS_SUBMODEL_ID 0x00
+#define BIOS_REVISION 1
+#define BIOS_CONFIG_TABLE 0xe6f5
+
+#ifndef BIOS_BUILD_DATE
+# define BIOS_BUILD_DATE "06/23/99"
+#endif
+
+ // 1K of base memory used for Extended Bios Data Area (EBDA)
+ // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
+#define EBDA_SEG 0x9FC0
+#define EBDA_SIZE 1 // In KiB
+#define BASE_MEM_IN_K (640 - EBDA_SIZE)
+
+/* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
+#define IPL_SEG 0x9ff0
+#define IPL_TABLE_OFFSET 0x0000
+#define IPL_TABLE_ENTRIES 8
+#define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
+#define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
+#define IPL_BOOTFIRST_OFFSET 0x0084 /* u16: user selected device */
+#define IPL_SIZE 0xff
+#define IPL_TYPE_FLOPPY 0x01
+#define IPL_TYPE_HARDDISK 0x02
+#define IPL_TYPE_CDROM 0x03
+#define IPL_TYPE_BEV 0x80
+
+ // Sanity Checks
+#if BX_USE_ATADRV && BX_CPU<3
+# error The ATA/ATAPI Driver can only to be used with a 386+ cpu
+#endif
+#if BX_USE_ATADRV && !BX_USE_EBDA
+# error ATA/ATAPI Driver can only be used if EBDA is available
+#endif
+#if BX_ELTORITO_BOOT && !BX_USE_ATADRV
+# error El-Torito Boot can only be use if ATA/ATAPI Driver is available
+#endif
+#if BX_PCIBIOS && BX_CPU<3
+# error PCI BIOS can only be used with 386+ cpu
+#endif
+#if BX_APM && BX_CPU<3
+# error APM BIOS can only be used with 386+ cpu
+#endif
+
+// define this if you want to make PCIBIOS working on a specific bridge only
+// undef enables PCIBIOS when at least one PCI device is found
+// i440FX is emulated by Bochs and QEMU
+#define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
+
+// #20 is dec 20
+// #$20 is hex 20 = 32
+// #0x20 is hex 20 = 32
+// LDA #$20
+// JSR $E820
+// LDD .i,S
+// JSR $C682
+// mov al, #$20
+
+// all hex literals should be prefixed with '0x'
+// grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
+// no mov SEG-REG, #value, must mov register into seg-reg
+// grep -i "mov[ ]*.s" rombios.c
+
+// This is for compiling with gcc2 and gcc3
+#define ASM_START #asm
+#define ASM_END #endasm
+
+ASM_START
+.rom
+
+.org 0x0000
+
+#if BX_CPU >= 3
+use16 386
+#else
+use16 286
+#endif
+
+MACRO HALT
+ ;; the HALT macro is called with the line number of the HALT call.
+ ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
+ ;; to print a BX_PANIC message. This will normally halt the simulation
+ ;; with a message such as "BIOS panic at rombios.c, line 4091".
+ ;; However, users can choose to make panics non-fatal and continue.
+#if BX_VIRTUAL_PORTS
+ mov dx,#PANIC_PORT
+ mov ax,#?1
+ out dx,ax
+#else
+ mov dx,#0x80
+ mov ax,#?1
+ out dx,al
+#endif
+MEND
+
+MACRO JMP_AP
+ db 0xea
+ dw ?2
+ dw ?1
+MEND
+
+MACRO SET_INT_VECTOR
+ mov ax, ?3
+ mov ?1*4, ax
+ mov ax, ?2
+ mov ?1*4+2, ax
+MEND
+
+ASM_END
+
+typedef unsigned char Bit8u;
+typedef unsigned short Bit16u;
+typedef unsigned short bx_bool;
+typedef unsigned long Bit32u;
+
+
+ void memsetb(seg,offset,value,count);
+ void memcpyb(dseg,doffset,sseg,soffset,count);
+ void memcpyd(dseg,doffset,sseg,soffset,count);
+
+ // memset of count bytes
+ void
+ memsetb(seg,offset,value,count)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u value;
+ Bit16u count;
+ {
+ ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+
+ mov cx, 10[bp] ; count
+ test cx, cx
+ je memsetb_end
+ mov ax, 4[bp] ; segment
+ mov es, ax
+ mov ax, 6[bp] ; offset
+ mov di, ax
+ mov al, 8[bp] ; value
+ cld
+ rep
+ stosb
+
+ memsetb_end:
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ ASM_END
+ }
+
+ // memcpy of count bytes
+ void
+ memcpyb(dseg,doffset,sseg,soffset,count)
+ Bit16u dseg;
+ Bit16u doffset;
+ Bit16u sseg;
+ Bit16u soffset;
+ Bit16u count;
+ {
+ ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+ push ds
+ push si
+
+ mov cx, 12[bp] ; count
+ test cx, cx
+ je memcpyb_end
+ mov ax, 4[bp] ; dsegment
+ mov es, ax
+ mov ax, 6[bp] ; doffset
+ mov di, ax
+ mov ax, 8[bp] ; ssegment
+ mov ds, ax
+ mov ax, 10[bp] ; soffset
+ mov si, ax
+ cld
+ rep
+ movsb
+
+ memcpyb_end:
+ pop si
+ pop ds
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ ASM_END
+ }
+
+ // memcpy of count dword
+ void
+ memcpyd(dseg,doffset,sseg,soffset,count)
+ Bit16u dseg;
+ Bit16u doffset;
+ Bit16u sseg;
+ Bit16u soffset;
+ Bit16u count;
+ {
+ ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+ push ds
+ push si
+
+ mov cx, 12[bp] ; count
+ test cx, cx
+ je memcpyd_end
+ mov ax, 4[bp] ; dsegment
+ mov es, ax
+ mov ax, 6[bp] ; doffset
+ mov di, ax
+ mov ax, 8[bp] ; ssegment
+ mov ds, ax
+ mov ax, 10[bp] ; soffset
+ mov si, ax
+ cld
+ rep
+ movsd
+
+ memcpyd_end:
+ pop si
+ pop ds
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ ASM_END
+ }
+
+ // read_dword and write_dword functions
+ static Bit32u read_dword();
+ static void write_dword();
+
+ Bit32u
+ read_dword(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+ {
+ ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, [bx]
+ add bx, #2
+ mov dx, [bx]
+ ;; ax = return value (word)
+ ;; dx = return value (word)
+ pop ds
+ pop bx
+
+ pop bp
+ ASM_END
+ }
+
+ void
+ write_dword(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit32u data;
+ {
+ ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, 8[bp] ; data word
+ mov [bx], ax ; write data word
+ add bx, #2
+ mov ax, 10[bp] ; data word
+ mov [bx], ax ; write data word
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ ASM_END
+ }
+
+ // Bit32u (unsigned long) and long helper functions
+ ASM_START
+
+ ;; and function
+ landl:
+ landul:
+ SEG SS
+ and ax,[di]
+ SEG SS
+ and bx,2[di]
+ ret
+
+ ;; add function
+ laddl:
+ laddul:
+ SEG SS
+ add ax,[di]
+ SEG SS
+ adc bx,2[di]
+ ret
+
+ ;; cmp function
+ lcmpl:
+ lcmpul:
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ or eax, ebx
+ shr ebx, #16
+ SEG SS
+ cmp eax, dword ptr [di]
+ ret
+
+ ;; sub function
+ lsubl:
+ lsubul:
+ SEG SS
+ sub ax,[di]
+ SEG SS
+ sbb bx,2[di]
+ ret
+
+ ;; mul function
+ lmull:
+ lmulul:
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ or eax, ebx
+ SEG SS
+ mul eax, dword ptr [di]
+ mov ebx, eax
+ shr ebx, #16
+ ret
+
+ ;; dec function
+ ldecl:
+ ldecul:
+ SEG SS
+ dec dword ptr [bx]
+ ret
+
+ ;; or function
+ lorl:
+ lorul:
+ SEG SS
+ or ax,[di]
+ SEG SS
+ or bx,2[di]
+ ret
+
+ ;; inc function
+ lincl:
+ lincul:
+ SEG SS
+ inc dword ptr [bx]
+ ret
+
+ ;; tst function
+ ltstl:
+ ltstul:
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ or eax, ebx
+ shr ebx, #16
+ test eax, eax
+ ret
+
+ ;; sr function
+ lsrul:
+ mov cx,di
+ jcxz lsr_exit
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ or eax, ebx
+ lsr_loop:
+ shr eax, #1
+ loop lsr_loop
+ mov ebx, eax
+ shr ebx, #16
+ lsr_exit:
+ ret
+
+ ;; sl function
+ lsll:
+ lslul:
+ mov cx,di
+ jcxz lsl_exit
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ or eax, ebx
+ lsl_loop:
+ shl eax, #1
+ loop lsl_loop
+ mov ebx, eax
+ shr ebx, #16
+ lsl_exit:
+ ret
+
+ idiv_:
+ cwd
+ idiv bx
+ ret
+
+ idiv_u:
+ xor dx,dx
+ div bx
+ ret
+
+ ldivul:
+ and eax, #0x0000FFFF
+ shl ebx, #16
+ or eax, ebx
+ xor edx, edx
+ SEG SS
+ mov bx, 2[di]
+ shl ebx, #16
+ SEG SS
+ mov bx, [di]
+ div ebx
+ mov ebx, eax
+ shr ebx, #16
+ ret
+
+ ASM_END
+
+// for access to RAM area which is used by interrupt vectors
+// and BIOS Data Area
+
+typedef struct {
+ unsigned char filler1[0x400];
+ unsigned char filler2[0x6c];
+ Bit16u ticks_low;
+ Bit16u ticks_high;
+ Bit8u midnight_flag;
+ } bios_data_t;
+
+#define BiosData ((bios_data_t *) 0)
+
+#if BX_USE_ATADRV
+ typedef struct {
+ Bit16u heads; // # heads
+ Bit16u cylinders; // # cylinders
+ Bit16u spt; // # sectors / track
+ } chs_t;
+
+ // DPTE definition
+ typedef struct {
+ Bit16u iobase1;
+ Bit16u iobase2;
+ Bit8u prefix;
+ Bit8u unused;
+ Bit8u irq;
+ Bit8u blkcount;
+ Bit8u dma;
+ Bit8u pio;
+ Bit16u options;
+ Bit16u reserved;
+ Bit8u revision;
+ Bit8u checksum;
+ } dpte_t;
+
+ typedef struct {
+ Bit8u iface; // ISA or PCI
+ Bit16u iobase1; // IO Base 1
+ Bit16u iobase2; // IO Base 2
+ Bit8u irq; // IRQ
+ } ata_channel_t;
+
+ typedef struct {
+ Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
+ Bit8u device; // Detected type of attached devices (hd/cd/none)
+ Bit8u removable; // Removable device flag
+ Bit8u lock; // Locks for removable devices
+ Bit8u mode; // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
+ Bit16u blksize; // block size
+
+ Bit8u translation; // type of translation
+ chs_t lchs; // Logical CHS
+ chs_t pchs; // Physical CHS
+
+ Bit32u sectors_low; // Total sectors count
+ Bit32u sectors_high;
+ } ata_device_t;
+
+ typedef struct {
+ // ATA channels info
+ ata_channel_t channels[BX_MAX_ATA_INTERFACES];
+
+ // ATA devices info
+ ata_device_t devices[BX_MAX_ATA_DEVICES];
+ //
+ // map between (bios hd id - 0x80) and ata channels
+ Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
+
+ // map between (bios cd id - 0xE0) and ata channels
+ Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
+
+ // Buffer for DPTE table
+ dpte_t dpte;
+
+ // Count of transferred sectors and bytes
+ Bit16u trsfsectors;
+ Bit32u trsfbytes;
+
+ } ata_t;
+
+#if BX_ELTORITO_BOOT
+ // ElTorito Device Emulation data
+ typedef struct {
+ Bit8u active;
+ Bit8u media;
+ Bit8u emulated_drive;
+ Bit8u controller_index;
+ Bit16u device_spec;
+ Bit32u ilba;
+ Bit16u buffer_segment;
+ Bit16u load_segment;
+ Bit16u sector_count;
+
+ // Virtual device
+ chs_t vdevice;
+ } cdemu_t;
+#endif // BX_ELTORITO_BOOT
+
+ // for access to EBDA area
+ // The EBDA structure should conform to
+ // http://www.frontiernet.net/~fys/rombios.htm document
+ // I made the ata and cdemu structs begin at 0x121 in the EBDA seg
+ // EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot
+ // device tables are at IPL_SEG
+ typedef struct {
+ unsigned char filler1[0x3D];
+
+ // FDPT - Can be splitted in data members if needed
+ unsigned char fdpt0[0x10];
+ unsigned char fdpt1[0x10];
+
+ unsigned char filler2[0xC4];
+
+ // ATA Driver data
+ ata_t ata;
+
+#if BX_ELTORITO_BOOT
+ // El Torito Emulation data
+ cdemu_t cdemu;
+#endif // BX_ELTORITO_BOOT
+
+ } ebda_data_t;
+
+ #define EbdaData ((ebda_data_t *) 0)
+
+ // for access to the int13ext structure
+ typedef struct {
+ Bit8u size;
+ Bit8u reserved;
+ Bit16u count;
+ Bit16u offset;
+ Bit16u segment;
+ Bit32u lba1;
+ Bit32u lba2;
+ } int13ext_t;
+
+ #define Int13Ext ((int13ext_t *) 0)
+
+ // Disk Physical Table definition
+ typedef struct {
+ Bit16u size;
+ Bit16u infos;
+ Bit32u cylinders;
+ Bit32u heads;
+ Bit32u spt;
+ Bit32u sector_count1;
+ Bit32u sector_count2;
+ Bit16u blksize;
+ Bit16u dpte_offset;
+ Bit16u dpte_segment;
+ Bit16u key;
+ Bit8u dpi_length;
+ Bit8u reserved1;
+ Bit16u reserved2;
+ Bit8u host_bus[4];
+ Bit8u iface_type[8];
+ Bit8u iface_path[8];
+ Bit8u device_path[8];
+ Bit8u reserved3;
+ Bit8u checksum;
+ } dpt_t;
+
+ #define Int13DPT ((dpt_t *) 0)
+
+#endif // BX_USE_ATADRV
+
+typedef struct {
+ union {
+ struct {
+ Bit16u di, si, bp, sp;
+ Bit16u bx, dx, cx, ax;
+ } r16;
+ struct {
+ Bit16u filler[4];
+ Bit8u bl, bh, dl, dh, cl, ch, al, ah;
+ } r8;
+ } u;
+ } pusha_regs_t;
+
+typedef struct {
+ union {
+ struct {
+ Bit32u edi, esi, ebp, esp;
+ Bit32u ebx, edx, ecx, eax;
+ } r32;
+ struct {
+ Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
+ Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
+ } r16;
+ struct {
+ Bit32u filler[4];
+ Bit8u bl, bh;
+ Bit16u filler1;
+ Bit8u dl, dh;
+ Bit16u filler2;
+ Bit8u cl, ch;
+ Bit16u filler3;
+ Bit8u al, ah;
+ Bit16u filler4;
+ } r8;
+ } u;
+} pushad_regs_t;
+
+typedef struct {
+ union {
+ struct {
+ Bit16u flags;
+ } r16;
+ struct {
+ Bit8u flagsl;
+ Bit8u flagsh;
+ } r8;
+ } u;
+ } flags_t;
+
+#define SetCF(x) x.u.r8.flagsl |= 0x01
+#define SetZF(x) x.u.r8.flagsl |= 0x40
+#define ClearCF(x) x.u.r8.flagsl &= 0xfe
+#define ClearZF(x) x.u.r8.flagsl &= 0xbf
+#define GetCF(x) (x.u.r8.flagsl & 0x01)
+
+typedef struct {
+ Bit16u ip;
+ Bit16u cs;
+ flags_t flags;
+ } iret_addr_t;
+
+typedef struct {
+ Bit16u type;
+ Bit16u flags;
+ Bit32u vector;
+ Bit32u description;
+ Bit32u reserved;
+ } ipl_entry_t;
+
+
+
+static Bit8u inb();
+static Bit8u inb_cmos();
+static void outb();
+static void outb_cmos();
+static Bit16u inw();
+static void outw();
+static void init_rtc();
+static bx_bool rtc_updating();
+
+static Bit8u read_byte();
+static Bit16u read_word();
+static void write_byte();
+static void write_word();
+static void bios_printf();
+
+static Bit8u inhibit_mouse_int_and_events();
+static void enable_mouse_int_and_events();
+static Bit8u send_to_mouse_ctrl();
+static Bit8u get_mouse_data();
+static void set_kbd_command_byte();
+
+static void int09_function();
+static void int13_harddisk();
+static void int13_cdrom();
+static void int13_cdemu();
+static void int13_eltorito();
+static void int13_diskette_function();
+static void int14_function();
+static void int15_function();
+static void int16_function();
+static void int17_function();
+static void int19_function();
+static void int1a_function();
+static void int70_function();
+static void int74_function();
+static Bit16u get_CS();
+static Bit16u get_SS();
+static unsigned int enqueue_key();
+static unsigned int dequeue_key();
+static void get_hd_geometry();
+static void set_diskette_ret_status();
+static void set_diskette_current_cyl();
+static void determine_floppy_media();
+static bx_bool floppy_drive_exists();
+static bx_bool floppy_drive_recal();
+static bx_bool floppy_media_known();
+static bx_bool floppy_media_sense();
+static bx_bool set_enable_a20();
+static void debugger_on();
+static void debugger_off();
+static void keyboard_init();
+static void keyboard_panic();
+static void shutdown_status_panic();
+static void nmi_handler_msg();
+static void delay_ticks();
+static void delay_ticks_and_check_for_keystroke();
+
+static void interactive_bootkey();
+static void print_bios_banner();
+static void print_boot_device();
+static void print_boot_failure();
+static void print_cdromboot_failure();
+
+# if BX_USE_ATADRV
+
+// ATA / ATAPI driver
+void ata_init();
+void ata_detect();
+void ata_reset();
+
+Bit16u ata_cmd_non_data();
+Bit16u ata_cmd_data_in();
+Bit16u ata_cmd_data_out();
+Bit16u ata_cmd_packet();
+
+Bit16u atapi_get_sense();
+Bit16u atapi_is_ready();
+Bit16u atapi_is_cdrom();
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+
+void cdemu_init();
+Bit8u cdemu_isactive();
+Bit8u cdemu_emulated_drive();
+
+Bit16u cdrom_boot();
+
+#endif // BX_ELTORITO_BOOT
+
+static char bios_cvs_version_string[] = "$Revision: 1.182 $ $Date: 2007/08/01 17:09:51 $";
+
+#define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
+
+#if DEBUG_ATA
+# define BX_DEBUG_ATA(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_ATA(a...)
+#endif
+#if DEBUG_INT13_HD
+# define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT13_HD(a...)
+#endif
+#if DEBUG_INT13_CD
+# define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT13_CD(a...)
+#endif
+#if DEBUG_INT13_ET
+# define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT13_ET(a...)
+#endif
+#if DEBUG_INT13_FL
+# define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT13_FL(a...)
+#endif
+#if DEBUG_INT15
+# define BX_DEBUG_INT15(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT15(a...)
+#endif
+#if DEBUG_INT16
+# define BX_DEBUG_INT16(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT16(a...)
+#endif
+#if DEBUG_INT1A
+# define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT1A(a...)
+#endif
+#if DEBUG_INT74
+# define BX_DEBUG_INT74(a...) BX_DEBUG(a)
+#else
+# define BX_DEBUG_INT74(a...)
+#endif
+
+#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
+#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
+#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
+#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
+#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
+#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
+#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
+#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
+
+#define GET_AL() ( AX & 0x00ff )
+#define GET_BL() ( BX & 0x00ff )
+#define GET_CL() ( CX & 0x00ff )
+#define GET_DL() ( DX & 0x00ff )
+#define GET_AH() ( AX >> 8 )
+#define GET_BH() ( BX >> 8 )
+#define GET_CH() ( CX >> 8 )
+#define GET_DH() ( DX >> 8 )
+
+#define GET_ELDL() ( ELDX & 0x00ff )
+#define GET_ELDH() ( ELDX >> 8 )
+
+#define SET_CF() FLAGS |= 0x0001
+#define CLEAR_CF() FLAGS &= 0xfffe
+#define GET_CF() (FLAGS & 0x0001)
+
+#define SET_ZF() FLAGS |= 0x0040
+#define CLEAR_ZF() FLAGS &= 0xffbf
+#define GET_ZF() (FLAGS & 0x0040)
+
+#define UNSUPPORTED_FUNCTION 0x86
+
+#define none 0
+#define MAX_SCAN_CODE 0x58
+
+static struct {
+ Bit16u normal;
+ Bit16u shift;
+ Bit16u control;
+ Bit16u alt;
+ Bit8u lock_flags;
+ } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
+ { none, none, none, none, none },
+ { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
+ { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
+ { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
+ { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
+ { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
+ { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
+ { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
+ { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
+ { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
+ { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
+ { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
+ { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
+ { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
+ { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
+ { 0x0f09, 0x0f00, none, none, none }, /* tab */
+ { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
+ { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
+ { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
+ { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
+ { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
+ { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
+ { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
+ { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
+ { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
+ { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
+ { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
+ { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
+ { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
+ { none, none, none, none, none }, /* L Ctrl */
+ { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
+ { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
+ { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
+ { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
+ { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
+ { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
+ { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
+ { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
+ { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
+ { 0x273b, 0x273a, none, none, none }, /* ;: */
+ { 0x2827, 0x2822, none, none, none }, /* '" */
+ { 0x2960, 0x297e, none, none, none }, /* `~ */
+ { none, none, none, none, none }, /* L shift */
+ { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
+ { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
+ { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
+ { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
+ { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
+ { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
+ { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
+ { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
+ { 0x332c, 0x333c, none, none, none }, /* ,< */
+ { 0x342e, 0x343e, none, none, none }, /* .> */
+ { 0x352f, 0x353f, none, none, none }, /* /? */
+ { none, none, none, none, none }, /* R Shift */
+ { 0x372a, 0x372a, none, none, none }, /* * */
+ { none, none, none, none, none }, /* L Alt */
+ { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
+ { none, none, none, none, none }, /* caps lock */
+ { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
+ { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
+ { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
+ { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
+ { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
+ { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
+ { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
+ { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
+ { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
+ { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
+ { none, none, none, none, none }, /* Num Lock */
+ { none, none, none, none, none }, /* Scroll Lock */
+ { 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
+ { 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
+ { 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
+ { 0x4a2d, 0x4a2d, none, none, none }, /* - */
+ { 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
+ { 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
+ { 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
+ { 0x4e2b, 0x4e2b, none, none, none }, /* + */
+ { 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
+ { 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
+ { 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
+ { 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
+ { 0x5300, 0x532e, none, none, 0x20 }, /* Del */
+ { none, none, none, none, none },
+ { none, none, none, none, none },
+ { 0x565c, 0x567c, none, none, none }, /* \| */
+ { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
+ { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
+ };
+
+ Bit8u
+inb(port)
+ Bit16u port;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push dx
+ mov dx, 4[bp]
+ in al, dx
+ pop dx
+
+ pop bp
+ASM_END
+}
+
+#if BX_USE_ATADRV
+ Bit16u
+inw(port)
+ Bit16u port;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push dx
+ mov dx, 4[bp]
+ in ax, dx
+ pop dx
+
+ pop bp
+ASM_END
+}
+#endif
+
+ void
+outb(port, val)
+ Bit16u port;
+ Bit8u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push dx
+ mov dx, 4[bp]
+ mov al, 6[bp]
+ out dx, al
+ pop dx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+#if BX_USE_ATADRV
+ void
+outw(port, val)
+ Bit16u port;
+ Bit16u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push dx
+ mov dx, 4[bp]
+ mov ax, 6[bp]
+ out dx, ax
+ pop dx
+ pop ax
+
+ pop bp
+ASM_END
+}
+#endif
+
+ void
+outb_cmos(cmos_reg, val)
+ Bit8u cmos_reg;
+ Bit8u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ mov al, 4[bp] ;; cmos_reg
+ out 0x70, al
+ mov al, 6[bp] ;; val
+ out 0x71, al
+
+ pop bp
+ASM_END
+}
+
+ Bit8u
+inb_cmos(cmos_reg)
+ Bit8u cmos_reg;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ mov al, 4[bp] ;; cmos_reg
+ out 0x70, al
+ in al, 0x71
+
+ pop bp
+ASM_END
+}
+
+ void
+init_rtc()
+{
+ outb_cmos(0x0a, 0x26);
+ outb_cmos(0x0b, 0x02);
+ inb_cmos(0x0c);
+ inb_cmos(0x0d);
+}
+
+ bx_bool
+rtc_updating()
+{
+ // This function checks to see if the update-in-progress bit
+ // is set in CMOS Status Register A. If not, it returns 0.
+ // If it is set, it tries to wait until there is a transition
+ // to 0, and will return 0 if such a transition occurs. A 1
+ // is returned only after timing out. The maximum period
+ // that this bit should be set is constrained to 244useconds.
+ // The count I use below guarantees coverage or more than
+ // this time, with any reasonable IPS setting.
+
+ Bit16u count;
+
+ count = 25000;
+ while (--count != 0) {
+ if ( (inb_cmos(0x0a) & 0x80) == 0 )
+ return(0);
+ }
+ return(1); // update-in-progress never transitioned to 0
+}
+
+
+ Bit8u
+read_byte(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov al, [bx]
+ ;; al = return value (byte)
+ pop ds
+ pop bx
+
+ pop bp
+ASM_END
+}
+
+ Bit16u
+read_word(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, [bx]
+ ;; ax = return value (word)
+ pop ds
+ pop bx
+
+ pop bp
+ASM_END
+}
+
+ void
+write_byte(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit8u data;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov al, 8[bp] ; data byte
+ mov [bx], al ; write data byte
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+ void
+write_word(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u data;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, 8[bp] ; data word
+ mov [bx], ax ; write data word
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+ Bit16u
+get_CS()
+{
+ASM_START
+ mov ax, cs
+ASM_END
+}
+
+ Bit16u
+get_SS()
+{
+ASM_START
+ mov ax, ss
+ASM_END
+}
+
+#if BX_DEBUG_SERIAL
+/* serial debug port*/
+#define BX_DEBUG_PORT 0x03f8
+
+/* data */
+#define UART_RBR 0x00
+#define UART_THR 0x00
+
+/* control */
+#define UART_IER 0x01
+#define UART_IIR 0x02
+#define UART_FCR 0x02
+#define UART_LCR 0x03
+#define UART_MCR 0x04
+#define UART_DLL 0x00
+#define UART_DLM 0x01
+
+/* status */
+#define UART_LSR 0x05
+#define UART_MSR 0x06
+#define UART_SCR 0x07
+
+int uart_can_tx_byte(base_port)
+ Bit16u base_port;
+{
+ return inb(base_port + UART_LSR) & 0x20;
+}
+
+void uart_wait_to_tx_byte(base_port)
+ Bit16u base_port;
+{
+ while (!uart_can_tx_byte(base_port));
+}
+
+void uart_wait_until_sent(base_port)
+ Bit16u base_port;
+{
+ while (!(inb(base_port + UART_LSR) & 0x40));
+}
+
+void uart_tx_byte(base_port, data)
+ Bit16u base_port;
+ Bit8u data;
+{
+ uart_wait_to_tx_byte(base_port);
+ outb(base_port + UART_THR, data);
+ uart_wait_until_sent(base_port);
+}
+#endif
+
+ void
+wrch(c)
+ Bit8u c;
+{
+ ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ mov ah, #0x0e
+ mov al, 4[bp]
+ xor bx,bx
+ int #0x10
+ pop bx
+
+ pop bp
+ ASM_END
+}
+
+ void
+send(action, c)
+ Bit16u action;
+ Bit8u c;
+{
+#if BX_DEBUG_SERIAL
+ if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
+ uart_tx_byte(BX_DEBUG_PORT, c);
+#endif
+#if BX_VIRTUAL_PORTS
+ if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
+ if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
+#endif
+ if (action & BIOS_PRINTF_SCREEN) {
+ if (c == '\n') wrch('\r');
+ wrch(c);
+ }
+}
+
+ void
+put_int(action, val, width, neg)
+ Bit16u action;
+ short val, width;
+ bx_bool neg;
+{
+ short nval = val / 10;
+ if (nval)
+ put_int(action, nval, width - 1, neg);
+ else {
+ while (--width > 0) send(action, ' ');
+ if (neg) send(action, '-');
+ }
+ send(action, val - (nval * 10) + '0');
+}
+
+ void
+put_uint(action, val, width, neg)
+ Bit16u action;
+ unsigned short val;
+ short width;
+ bx_bool neg;
+{
+ unsigned short nval = val / 10;
+ if (nval)
+ put_uint(action, nval, width - 1, neg);
+ else {
+ while (--width > 0) send(action, ' ');
+ if (neg) send(action, '-');
+ }
+ send(action, val - (nval * 10) + '0');
+}
+
+ void
+put_luint(action, val, width, neg)
+ Bit16u action;
+ unsigned long val;
+ short width;
+ bx_bool neg;
+{
+ unsigned long nval = val / 10;
+ if (nval)
+ put_luint(action, nval, width - 1, neg);
+ else {
+ while (--width > 0) send(action, ' ');
+ if (neg) send(action, '-');
+ }
+ send(action, val - (nval * 10) + '0');
+}
+
+void put_str(action, segment, offset)
+ Bit16u action;
+ Bit16u segment;
+ Bit16u offset;
+{
+ Bit8u c;
+
+ while (c = read_byte(segment, offset)) {
+ send(action, c);
+ offset++;
+ }
+}
+
+ void
+delay_ticks(ticks)
+ Bit16u ticks;
+{
+ long ticks_to_wait, delta;
+ Bit32u prev_ticks, t;
+
+ /*
+ * The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
+ * We also have to be careful about interrupt storms.
+ */
+ASM_START
+ pushf
+ sti
+ASM_END
+ ticks_to_wait = ticks;
+ prev_ticks = read_dword(0x0, 0x46c);
+ do
+ {
+ASM_START
+ hlt
+ASM_END
+ t = read_dword(0x0, 0x46c);
+ if (t > prev_ticks)
+ {
+ delta = t - prev_ticks; /* The temp var is required or bcc screws up. */
+ ticks_to_wait -= delta;
+ }
+ else if (t < prev_ticks)
+ {
+ ticks_to_wait -= t; /* wrapped */
+ }
+
+ prev_ticks = t;
+ } while (ticks_to_wait > 0);
+ASM_START
+ cli
+ popf
+ASM_END
+}
+
+ Bit8u
+check_for_keystroke()
+{
+ASM_START
+ mov ax, #0x100
+ int #0x16
+ jz no_key
+ mov al, #1
+ jmp done
+no_key:
+ xor al, al
+done:
+ASM_END
+}
+
+ Bit8u
+get_keystroke()
+{
+ASM_START
+ mov ax, #0x0
+ int #0x16
+ xchg ah, al
+ASM_END
+}
+
+ void
+delay_ticks_and_check_for_keystroke(ticks, count)
+ Bit16u ticks, count;
+{
+ Bit16u i;
+ for (i = 1; i <= count; i++) {
+ delay_ticks(ticks);
+ if (check_for_keystroke())
+ break;
+ }
+}
+
+//--------------------------------------------------------------------------
+// bios_printf()
+// A compact variable argument printf function.
+//
+// Supports %[format_width][length]format
+// where format can be x,X,u,d,s,S,c
+// and the optional length modifier is l (ell)
+//--------------------------------------------------------------------------
+ void
+bios_printf(action, s)
+ Bit16u action;
+ Bit8u *s;
+{
+ Bit8u c, format_char;
+ bx_bool in_format;
+ short i;
+ Bit16u *arg_ptr;
+ Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
+
+ arg_ptr = &s;
+ arg_seg = get_SS();
+
+ in_format = 0;
+ format_width = 0;
+
+ if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
+#if BX_VIRTUAL_PORTS
+ outb(PANIC_PORT2, 0x00);
+#endif
+ bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
+ }
+
+ while (c = read_byte(get_CS(), s)) {
+ if ( c == '%' ) {
+ in_format = 1;
+ format_width = 0;
+ }
+ else if (in_format) {
+ if ( (c>='0') && (c<='9') ) {
+ format_width = (format_width * 10) + (c - '0');
+ }
+ else {
+ arg_ptr++; // increment to next arg
+ arg = read_word(arg_seg, arg_ptr);
+ if (c == 'x' || c == 'X') {
+ if (format_width == 0)
+ format_width = 4;
+ if (c == 'x')
+ hexadd = 'a';
+ else
+ hexadd = 'A';
+ for (i=format_width-1; i>=0; i--) {
+ nibble = (arg >> (4 * i)) & 0x000f;
+ send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
+ }
+ }
+ else if (c == 'u') {
+ put_uint(action, arg, format_width, 0);
+ }
+ else if (c == 'l') {
+ s++;
+ c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
+ arg_ptr++; /* increment to next arg */
+ hibyte = read_word(arg_seg, arg_ptr);
+ if (c == 'd') {
+ if (hibyte & 0x8000)
+ put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
+ else
+ put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
+ }
+ else if (c == 'u') {
+ put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
+ }
+ else if (c == 'x' || c == 'X')
+ {
+ if (format_width == 0)
+ format_width = 8;
+ if (c == 'x')
+ hexadd = 'a';
+ else
+ hexadd = 'A';
+ for (i=format_width-1; i>=0; i--) {
+ nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
+ send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
+ }
+ }
+ }
+ else if (c == 'd') {
+ if (arg & 0x8000)
+ put_int(action, -arg, format_width - 1, 1);
+ else
+ put_int(action, arg, format_width, 0);
+ }
+ else if (c == 's') {
+ put_str(action, get_CS(), arg);
+ }
+ else if (c == 'S') {
+ hibyte = arg;
+ arg_ptr++;
+ arg = read_word(arg_seg, arg_ptr);
+ put_str(action, hibyte, arg);
+ }
+ else if (c == 'c') {
+ send(action, arg);
+ }
+ else
+ BX_PANIC("bios_printf: unknown format\n");
+ in_format = 0;
+ }
+ }
+ else {
+ send(action, c);
+ }
+ s ++;
+ }
+
+ if (action & BIOS_PRINTF_HALT) {
+ // freeze in a busy loop.
+ASM_START
+ cli
+ halt2_loop:
+ hlt
+ jmp halt2_loop
+ASM_END
+ }
+}
+
+//--------------------------------------------------------------------------
+// keyboard_init
+//--------------------------------------------------------------------------
+// this file is based on LinuxBIOS implementation of keyboard.c
+// could convert to #asm to gain space
+ void
+keyboard_init()
+{
+ Bit16u max;
+
+ /* ------------------- Flush buffers ------------------------*/
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
+
+ /* flush incoming keys */
+ max=0x2000;
+ while (--max > 0) {
+ outb(0x80, 0x00);
+ if (inb(0x64) & 0x01) {
+ inb(0x60);
+ max = 0x2000;
+ }
+ }
+
+ // Due to timer issues, and if the IPS setting is > 15000000,
+ // the incoming keys might not be flushed here. That will
+ // cause a panic a few lines below. See sourceforge bug report :
+ // [ 642031 ] FATAL: Keyboard RESET error:993
+
+ /* ------------------- controller side ----------------------*/
+ /* send cmd = 0xAA, self test 8042 */
+ outb(0x64, 0xaa);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
+ if (max==0x0) keyboard_panic(00);
+
+ /* Wait for data */
+ max=0xffff;
+ while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
+ if (max==0x0) keyboard_panic(01);
+
+ /* read self-test result, 0x55 should be returned from 0x60 */
+ if ((inb(0x60) != 0x55)){
+ keyboard_panic(991);
+ }
+
+ /* send cmd = 0xAB, keyboard interface test */
+ outb(0x64,0xab);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
+ if (max==0x0) keyboard_panic(10);
+
+ /* Wait for data */
+ max=0xffff;
+ while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
+ if (max==0x0) keyboard_panic(11);
+
+ /* read keyboard interface test result, */
+ /* 0x00 should be returned form 0x60 */
+ if ((inb(0x60) != 0x00)) {
+ keyboard_panic(992);
+ }
+
+ /* Enable Keyboard clock */
+ outb(0x64,0xae);
+ outb(0x64,0xa8);
+
+ /* ------------------- keyboard side ------------------------*/
+ /* reset kerboard and self test (keyboard side) */
+ outb(0x60, 0xff);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
+ if (max==0x0) keyboard_panic(20);
+
+ /* Wait for data */
+ max=0xffff;
+ while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
+ if (max==0x0) keyboard_panic(21);
+
+ /* keyboard should return ACK */
+ if ((inb(0x60) != 0xfa)) {
+ keyboard_panic(993);
+ }
+
+ /* Wait for data */
+ max=0xffff;
+ while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
+ if (max==0x0) keyboard_panic(31);
+
+ if ((inb(0x60) != 0xaa)) {
+ keyboard_panic(994);
+ }
+
+ /* Disable keyboard */
+ outb(0x60, 0xf5);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
+ if (max==0x0) keyboard_panic(40);
+
+ /* Wait for data */
+ max=0xffff;
+ while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
+ if (max==0x0) keyboard_panic(41);
+
+ /* keyboard should return ACK */
+ if ((inb(0x60) != 0xfa)) {
+ keyboard_panic(995);
+ }
+
+ /* Write Keyboard Mode */
+ outb(0x64, 0x60);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
+ if (max==0x0) keyboard_panic(50);
+
+ /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
+ outb(0x60, 0x61);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
+ if (max==0x0) keyboard_panic(60);
+
+ /* Enable keyboard */
+ outb(0x60, 0xf4);
+
+ /* Wait until buffer is empty */
+ max=0xffff;
+ while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
+ if (max==0x0) keyboard_panic(70);
+
+ /* Wait for data */
+ max=0xffff;
+ while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
+ if (max==0x0) keyboard_panic(70);
+
+ /* keyboard should return ACK */
+ if ((inb(0x60) != 0xfa)) {
+ keyboard_panic(996);
+ }
+
+ outb(0x80, 0x77);
+}
+
+//--------------------------------------------------------------------------
+// keyboard_panic
+//--------------------------------------------------------------------------
+ void
+keyboard_panic(status)
+ Bit16u status;
+{
+ // If you're getting a 993 keyboard panic here,
+ // please see the comment in keyboard_init
+
+ BX_PANIC("Keyboard error:%u\n",status);
+}
+
+//--------------------------------------------------------------------------
+// shutdown_status_panic
+// called when the shutdown statsu is not implemented, displays the status
+//--------------------------------------------------------------------------
+ void
+shutdown_status_panic(status)
+ Bit16u status;
+{
+ BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
+}
+
+void s3_resume_panic()
+{
+ BX_PANIC("Returned from s3_resume.\n");
+}
+
+//--------------------------------------------------------------------------
+// print_bios_banner
+// displays a the bios version
+//--------------------------------------------------------------------------
+void
+print_bios_banner()
+{
+ printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
+ BIOS_BUILD_DATE, bios_cvs_version_string);
+ printf(
+#if BX_APM
+ "apmbios "
+#endif
+#if BX_PCIBIOS
+ "pcibios "
+#endif
+#if BX_ELTORITO_BOOT
+ "eltorito "
+#endif
+#if BX_ROMBIOS32
+ "rombios32 "
+#endif
+ "\n\n");
+}
+
+//--------------------------------------------------------------------------
+// BIOS Boot Specification 1.0.1 compatibility
+//
+// Very basic support for the BIOS Boot Specification, which allows expansion
+// ROMs to register themselves as boot devices, instead of just stealing the
+// INT 19h boot vector.
+//
+// This is a hack: to do it properly requires a proper PnP BIOS and we aren't
+// one; we just lie to the option ROMs to make them behave correctly.
+// We also don't support letting option ROMs register as bootable disk
+// drives (BCVs), only as bootable devices (BEVs).
+//
+// http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
+//--------------------------------------------------------------------------
+
+static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
+
+static void
+init_boot_vectors()
+{
+ ipl_entry_t e;
+ Bit16u count = 0;
+ Bit16u ss = get_SS();
+
+ /* Clear out the IPL table. */
+ memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
+
+ /* User selected device not set */
+ write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
+
+ /* Floppy drive */
+ e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
+ memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
+ count++;
+
+ /* First HDD */
+ e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
+ memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
+ count++;
+
+#if BX_ELTORITO_BOOT
+ /* CDROM */
+ e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
+ memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
+ count++;
+#endif
+
+ /* Remember how many devices we have */
+ write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
+ /* Not tried booting anything yet */
+ write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
+}
+
+static Bit8u
+get_boot_vector(i, e)
+Bit16u i; ipl_entry_t *e;
+{
+ Bit16u count;
+ Bit16u ss = get_SS();
+ /* Get the count of boot devices, and refuse to overrun the array */
+ count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
+ if (i >= count) return 0;
+ /* OK to read this device */
+ memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
+ return 1;
+}
+
+#if BX_ELTORITO_BOOT
+ void
+interactive_bootkey()
+{
+ ipl_entry_t e;
+ Bit16u count;
+ char description[33];
+ Bit8u scan_code;
+ Bit8u i;
+ Bit16u ss = get_SS();
+ Bit16u valid_choice = 0;
+
+ while (check_for_keystroke())
+ get_keystroke();
+
+ printf("Press F12 for boot menu.\n\n");
+
+ delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */
+ if (check_for_keystroke())
+ {
+ scan_code = get_keystroke();
+ if (scan_code == 0x86) /* F12 */
+ {
+ while (check_for_keystroke())
+ get_keystroke();
+
+ printf("Select boot device:\n\n");
+
+ count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
+ for (i = 0; i < count; i++)
+ {
+ memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e));
+ printf("%d. ", i+1);
+ switch(e.type)
+ {
+ case IPL_TYPE_FLOPPY:
+ case IPL_TYPE_HARDDISK:
+ case IPL_TYPE_CDROM:
+ printf("%s\n", drivetypes[e.type]);
+ break;
+ case IPL_TYPE_BEV:
+ printf("%s", drivetypes[4]);
+ if (e.description != 0)
+ {
+ memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32);
+ description[32] = 0;
+ printf(" [%S]", ss, description);
+ }
+ printf("\n");
+ break;
+ }
+ }
+
+ count++;
+ while (!valid_choice) {
+ scan_code = get_keystroke();
+ if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */
+ {
+ valid_choice = 1;
+ }
+ else if (scan_code <= count)
+ {
+ valid_choice = 1;
+ scan_code -= 1;
+ /* Set user selected device */
+ write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code);
+ }
+ }
+ printf("\n");
+ }
+ }
+}
+#endif // BX_ELTORITO_BOOT
+
+//--------------------------------------------------------------------------
+// print_boot_device
+// displays the boot device
+//--------------------------------------------------------------------------
+
+void
+print_boot_device(e)
+ ipl_entry_t *e;
+{
+ Bit16u type;
+ char description[33];
+ Bit16u ss = get_SS();
+ type = e->type;
+ /* NIC appears as type 0x80 */
+ if (type == IPL_TYPE_BEV) type = 0x4;
+ if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
+ printf("Booting from %s", drivetypes[type]);
+ /* print product string if BEV */
+ if (type == 4 && e->description != 0) {
+ /* first 32 bytes are significant */
+ memcpyb(ss, &description, (Bit16u)(e->description >> 16), (Bit16u)(e->description & 0xffff), 32);
+ /* terminate string */
+ description[32] = 0;
+ printf(" [%S]", ss, description);
+ }
+ printf("...\n");
+}
+
+//--------------------------------------------------------------------------
+// print_boot_failure
+// displays the reason why boot failed
+//--------------------------------------------------------------------------
+ void
+print_boot_failure(type, reason)
+ Bit16u type; Bit8u reason;
+{
+ if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
+
+ printf("Boot failed");
+ if (type < 4) {
+ /* Report the reason too */
+ if (reason==0)
+ printf(": not a bootable disk");
+ else
+ printf(": could not read the boot disk");
+ }
+ printf("\n\n");
+}
+
+//--------------------------------------------------------------------------
+// print_cdromboot_failure
+// displays the reason why boot failed
+//--------------------------------------------------------------------------
+ void
+print_cdromboot_failure( code )
+ Bit16u code;
+{
+ bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
+
+ return;
+}
+
+void
+nmi_handler_msg()
+{
+ BX_PANIC("NMI Handler called\n");
+}
+
+void
+int18_panic_msg()
+{
+ BX_PANIC("INT18: BOOT FAILURE\n");
+}
+
+void
+log_bios_start()
+{
+#if BX_DEBUG_SERIAL
+ outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
+#endif
+ BX_INFO("%s\n", bios_cvs_version_string);
+}
+
+ bx_bool
+set_enable_a20(val)
+ bx_bool val;
+{
+ Bit8u oldval;
+
+ // Use PS2 System Control port A to set A20 enable
+
+ // get current setting first
+ oldval = inb(0x92);
+
+ // change A20 status
+ if (val)
+ outb(0x92, oldval | 0x02);
+ else
+ outb(0x92, oldval & 0xfd);
+
+ return((oldval & 0x02) != 0);
+}
+
+ void
+debugger_on()
+{
+ outb(0xfedc, 0x01);
+}
+
+ void
+debugger_off()
+{
+ outb(0xfedc, 0x00);
+}
+
+int
+s3_resume()
+{
+ Bit32u s3_wakeup_vector;
+ Bit8u s3_resume_flag;
+
+ s3_resume_flag = read_byte(0x40, 0xb0);
+ s3_wakeup_vector = read_dword(0x40, 0xb2);
+
+ BX_INFO("S3 resume called %x 0x%lx\n", s3_resume_flag, s3_wakeup_vector);
+ if (s3_resume_flag != 0xFE || !s3_wakeup_vector)
+ return 0;
+
+ write_byte(0x40, 0xb0, 0);
+
+ /* setup wakeup vector */
+ write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */
+ write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */
+
+ BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4),
+ (s3_wakeup_vector & 0xF));
+ASM_START
+ jmpf [0x04b6]
+ASM_END
+ return 1;
+}
+
+#if BX_USE_ATADRV
+
+// ---------------------------------------------------------------------------
+// Start of ATA/ATAPI Driver
+// ---------------------------------------------------------------------------
+
+// Global defines -- ATA register and register bits.
+// command block & control block regs
+#define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
+#define ATA_CB_ERR 1 // error in pio_base_addr1+1
+#define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
+#define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
+#define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
+#define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
+#define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
+#define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
+#define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
+#define ATA_CB_CMD 7 // command out pio_base_addr1+7
+#define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
+#define ATA_CB_DC 6 // device control out pio_base_addr2+6
+#define ATA_CB_DA 7 // device address in pio_base_addr2+7
+
+#define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
+#define ATA_CB_ER_BBK 0x80 // ATA bad block
+#define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
+#define ATA_CB_ER_MC 0x20 // ATA media change
+#define ATA_CB_ER_IDNF 0x10 // ATA id not found
+#define ATA_CB_ER_MCR 0x08 // ATA media change request
+#define ATA_CB_ER_ABRT 0x04 // ATA command aborted
+#define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
+#define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
+
+#define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
+#define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
+#define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
+#define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
+#define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
+
+// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
+#define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
+#define ATA_CB_SC_P_REL 0x04 // ATAPI release
+#define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
+#define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
+
+// bits 7-4 of the device/head (CB_DH) reg
+#define ATA_CB_DH_DEV0 0xa0 // select device 0
+#define ATA_CB_DH_DEV1 0xb0 // select device 1
+#define ATA_CB_DH_LBA 0x40 // use LBA
+
+// status reg (CB_STAT and CB_ASTAT) bits
+#define ATA_CB_STAT_BSY 0x80 // busy
+#define ATA_CB_STAT_RDY 0x40 // ready
+#define ATA_CB_STAT_DF 0x20 // device fault
+#define ATA_CB_STAT_WFT 0x20 // write fault (old name)
+#define ATA_CB_STAT_SKC 0x10 // seek complete
+#define ATA_CB_STAT_SERV 0x10 // service
+#define ATA_CB_STAT_DRQ 0x08 // data request
+#define ATA_CB_STAT_CORR 0x04 // corrected
+#define ATA_CB_STAT_IDX 0x02 // index
+#define ATA_CB_STAT_ERR 0x01 // error (ATA)
+#define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
+
+// device control reg (CB_DC) bits
+#define ATA_CB_DC_HD15 0x08 // bit should always be set to one
+#define ATA_CB_DC_SRST 0x04 // soft reset
+#define ATA_CB_DC_NIEN 0x02 // disable interrupts
+
+// Most mandtory and optional ATA commands (from ATA-3),
+#define ATA_CMD_CFA_ERASE_SECTORS 0xC0
+#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
+#define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
+#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
+#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
+#define ATA_CMD_CHECK_POWER_MODE1 0xE5
+#define ATA_CMD_CHECK_POWER_MODE2 0x98
+#define ATA_CMD_DEVICE_RESET 0x08
+#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
+#define ATA_CMD_FLUSH_CACHE 0xE7
+#define ATA_CMD_FORMAT_TRACK 0x50
+#define ATA_CMD_IDENTIFY_DEVICE 0xEC
+#define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
+#define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
+#define ATA_CMD_IDLE1 0xE3
+#define ATA_CMD_IDLE2 0x97
+#define ATA_CMD_IDLE_IMMEDIATE1 0xE1
+#define ATA_CMD_IDLE_IMMEDIATE2 0x95
+#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
+#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
+#define ATA_CMD_NOP 0x00
+#define ATA_CMD_PACKET 0xA0
+#define ATA_CMD_READ_BUFFER 0xE4
+#define ATA_CMD_READ_DMA 0xC8
+#define ATA_CMD_READ_DMA_QUEUED 0xC7
+#define ATA_CMD_READ_MULTIPLE 0xC4
+#define ATA_CMD_READ_SECTORS 0x20
+#define ATA_CMD_READ_VERIFY_SECTORS 0x40
+#define ATA_CMD_RECALIBRATE 0x10
+#define ATA_CMD_REQUEST_SENSE 0x03
+#define ATA_CMD_SEEK 0x70
+#define ATA_CMD_SET_FEATURES 0xEF
+#define ATA_CMD_SET_MULTIPLE_MODE 0xC6
+#define ATA_CMD_SLEEP1 0xE6
+#define ATA_CMD_SLEEP2 0x99
+#define ATA_CMD_STANDBY1 0xE2
+#define ATA_CMD_STANDBY2 0x96
+#define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
+#define ATA_CMD_STANDBY_IMMEDIATE2 0x94
+#define ATA_CMD_WRITE_BUFFER 0xE8
+#define ATA_CMD_WRITE_DMA 0xCA
+#define ATA_CMD_WRITE_DMA_QUEUED 0xCC
+#define ATA_CMD_WRITE_MULTIPLE 0xC5
+#define ATA_CMD_WRITE_SECTORS 0x30
+#define ATA_CMD_WRITE_VERIFY 0x3C
+
+#define ATA_IFACE_NONE 0x00
+#define ATA_IFACE_ISA 0x00
+#define ATA_IFACE_PCI 0x01
+
+#define ATA_TYPE_NONE 0x00
+#define ATA_TYPE_UNKNOWN 0x01
+#define ATA_TYPE_ATA 0x02
+#define ATA_TYPE_ATAPI 0x03
+
+#define ATA_DEVICE_NONE 0x00
+#define ATA_DEVICE_HD 0xFF
+#define ATA_DEVICE_CDROM 0x05
+
+#define ATA_MODE_NONE 0x00
+#define ATA_MODE_PIO16 0x00
+#define ATA_MODE_PIO32 0x01
+#define ATA_MODE_ISADMA 0x02
+#define ATA_MODE_PCIDMA 0x03
+#define ATA_MODE_USEIRQ 0x10
+
+#define ATA_TRANSLATION_NONE 0
+#define ATA_TRANSLATION_LBA 1
+#define ATA_TRANSLATION_LARGE 2
+#define ATA_TRANSLATION_RECHS 3
+
+#define ATA_DATA_NO 0x00
+#define ATA_DATA_IN 0x01
+#define ATA_DATA_OUT 0x02
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : initialization
+// ---------------------------------------------------------------------------
+void ata_init( )
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit8u channel, device;
+
+ // Channels info init.
+ for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
+ write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
+ write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
+ write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
+ write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
+ }
+
+ // Devices info init.
+ for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
+
+ write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L);
+ write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L);
+ }
+
+ // hdidmap and cdidmap init.
+ for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
+ write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
+ write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
+ }
+
+ write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
+ write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
+}
+
+#define TIMEOUT 0
+#define BSY 1
+#define NOT_BSY 2
+#define NOT_BSY_DRQ 3
+#define NOT_BSY_NOT_DRQ 4
+#define NOT_BSY_RDY 5
+
+#define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
+
+int await_ide();
+static int await_ide(when_done,base,timeout)
+ Bit8u when_done;
+ Bit16u base;
+ Bit16u timeout;
+{
+ Bit32u time=0,last=0;
+ Bit16u status;
+ Bit8u result;
+ status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
+ for(;;) {
+ status = inb(base+ATA_CB_STAT);
+ time++;
+ if (when_done == BSY)
+ result = status & ATA_CB_STAT_BSY;
+ else if (when_done == NOT_BSY)
+ result = !(status & ATA_CB_STAT_BSY);
+ else if (when_done == NOT_BSY_DRQ)
+ result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
+ else if (when_done == NOT_BSY_NOT_DRQ)
+ result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
+ else if (when_done == NOT_BSY_RDY)
+ result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
+ else if (when_done == TIMEOUT)
+ result = 0;
+
+ if (result) return 0;
+ if (time>>16 != last) // mod 2048 each 16 ms
+ {
+ last = time >>16;
+ BX_DEBUG_ATA("await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
+ }
+ if (status & ATA_CB_STAT_ERR)
+ {
+ BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
+ return -1;
+ }
+ if ((timeout == 0) || ((time>>11) > timeout)) break;
+ }
+ BX_INFO("IDE time out\n");
+ return -1;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : device detection
+// ---------------------------------------------------------------------------
+
+void ata_detect( )
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit8u hdcount, cdcount, device, type;
+ Bit8u buffer[0x0200];
+
+#if BX_MAX_ATA_INTERFACES > 0
+ write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
+ write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
+ write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
+ write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
+#endif
+#if BX_MAX_ATA_INTERFACES > 1
+ write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
+ write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
+ write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
+ write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
+#endif
+#if BX_MAX_ATA_INTERFACES > 2
+ write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
+ write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
+ write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
+ write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
+#endif
+#if BX_MAX_ATA_INTERFACES > 3
+ write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
+ write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
+ write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
+ write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
+#endif
+#if BX_MAX_ATA_INTERFACES > 4
+#error Please fill the ATA interface informations
+#endif
+
+ // Device detection
+ hdcount=cdcount=0;
+
+ for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
+ Bit16u iobase1, iobase2;
+ Bit8u channel, slave, shift;
+ Bit8u sc, sn, cl, ch, st;
+
+ channel = device / 2;
+ slave = device % 2;
+
+ iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
+ iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
+
+ // Disable interrupts
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+ // Look for device
+ outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
+ outb(iobase1+ATA_CB_SC, 0x55);
+ outb(iobase1+ATA_CB_SN, 0xaa);
+ outb(iobase1+ATA_CB_SC, 0xaa);
+ outb(iobase1+ATA_CB_SN, 0x55);
+ outb(iobase1+ATA_CB_SC, 0x55);
+ outb(iobase1+ATA_CB_SN, 0xaa);
+
+ // If we found something
+ sc = inb(iobase1+ATA_CB_SC);
+ sn = inb(iobase1+ATA_CB_SN);
+
+ if ( (sc == 0x55) && (sn == 0xaa) ) {
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
+
+ // reset the channel
+ ata_reset(device);
+
+ // check for ATA or ATAPI
+ outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
+ sc = inb(iobase1+ATA_CB_SC);
+ sn = inb(iobase1+ATA_CB_SN);
+ if ((sc==0x01) && (sn==0x01)) {
+ cl = inb(iobase1+ATA_CB_CL);
+ ch = inb(iobase1+ATA_CB_CH);
+ st = inb(iobase1+ATA_CB_STAT);
+
+ if ((cl==0x14) && (ch==0xeb)) {
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
+ } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
+ } else if ((cl==0xff) && (ch==0xff)) {
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
+ }
+ }
+ }
+
+ type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
+
+ // Now we send a IDENTIFY command to ATA device
+ if(type == ATA_TYPE_ATA) {
+ Bit32u sectors_low, sectors_high;
+ Bit16u cylinders, heads, spt, blksize;
+ Bit8u translation, removable, mode;
+
+ //Temporary values to do the transfer
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
+
+ if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 )
+ BX_PANIC("ata-detect: Failed to detect ATA device\n");
+
+ removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
+ mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
+ blksize = read_word(get_SS(),buffer+10);
+
+ cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
+ heads = read_word(get_SS(),buffer+(3*2)); // word 3
+ spt = read_word(get_SS(),buffer+(6*2)); // word 6
+
+ if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support
+ sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101
+ sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103
+ } else {
+ sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
+ sectors_high = 0;
+ }
+
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
+ write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low);
+ write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high);
+ BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
+
+ translation = inb_cmos(0x39 + channel/2);
+ for (shift=device%4; shift>0; shift--) translation >>= 2;
+ translation &= 0x03;
+
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
+
+ switch (translation) {
+ case ATA_TRANSLATION_NONE:
+ BX_INFO("none");
+ break;
+ case ATA_TRANSLATION_LBA:
+ BX_INFO("lba");
+ break;
+ case ATA_TRANSLATION_LARGE:
+ BX_INFO("large");
+ break;
+ case ATA_TRANSLATION_RECHS:
+ BX_INFO("r-echs");
+ break;
+ }
+ switch (translation) {
+ case ATA_TRANSLATION_NONE:
+ break;
+ case ATA_TRANSLATION_LBA:
+ spt = 63;
+ sectors_low /= 63;
+ heads = sectors_low / 1024;
+ if (heads>128) heads = 255;
+ else if (heads>64) heads = 128;
+ else if (heads>32) heads = 64;
+ else if (heads>16) heads = 32;
+ else heads=16;
+ cylinders = sectors_low / heads;
+ break;
+ case ATA_TRANSLATION_RECHS:
+ // Take care not to overflow
+ if (heads==16) {
+ if(cylinders>61439) cylinders=61439;
+ heads=15;
+ cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
+ }
+ // then go through the large bitshift process
+ case ATA_TRANSLATION_LARGE:
+ while(cylinders > 1024) {
+ cylinders >>= 1;
+ heads <<= 1;
+
+ // If we max out the head count
+ if (heads > 127) break;
+ }
+ break;
+ }
+ // clip to 1024 cylinders in lchs
+ if (cylinders > 1024) cylinders=1024;
+ BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
+
+ write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
+
+ // fill hdidmap
+ write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
+ hdcount++;
+ }
+
+ // Now we send a IDENTIFY command to ATAPI device
+ if(type == ATA_TYPE_ATAPI) {
+
+ Bit8u type, removable, mode;
+ Bit16u blksize;
+
+ //Temporary values to do the transfer
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
+
+ if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0)
+ BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
+
+ type = read_byte(get_SS(),buffer+1) & 0x1f;
+ removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
+ mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
+ blksize = 2048;
+
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
+ write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
+ write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
+
+ // fill cdidmap
+ write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
+ cdcount++;
+ }
+
+ {
+ Bit32u sizeinmb;
+ Bit16u ataversion;
+ Bit8u c, i, version, model[41];
+
+ switch (type) {
+ case ATA_TYPE_ATA:
+ sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21)
+ | (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11);
+ case ATA_TYPE_ATAPI:
+ // Read ATA/ATAPI version
+ ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
+ for(version=15;version>0;version--) {
+ if((ataversion&(1<<version))!=0)
+ break;
+ }
+
+ // Read model name
+ for(i=0;i<20;i++){
+ write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
+ write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
+ }
+
+ // Reformat
+ write_byte(get_SS(),model+40,0x00);
+ for(i=39;i>0;i--){
+ if(read_byte(get_SS(),model+i)==0x20)
+ write_byte(get_SS(),model+i,0x00);
+ else break;
+ }
+ if (i>36) {
+ write_byte(get_SS(),model+36,0x00);
+ for(i=35;i>32;i--){
+ write_byte(get_SS(),model+i,0x2E);
+ }
+ }
+ break;
+ }
+
+ switch (type) {
+ case ATA_TYPE_ATA:
+ printf("ata%d %s: ",channel,slave?" slave":"master");
+ i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
+ if (sizeinmb < (1UL<<16))
+ printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
+ else
+ printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
+ break;
+ case ATA_TYPE_ATAPI:
+ printf("ata%d %s: ",channel,slave?" slave":"master");
+ i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
+ if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
+ printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
+ else
+ printf(" ATAPI-%d Device\n",version);
+ break;
+ case ATA_TYPE_UNKNOWN:
+ printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
+ break;
+ }
+ }
+ }
+
+ // Store the devices counts
+ write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
+ write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
+ write_byte(0x40,0x75, hdcount);
+
+ printf("\n");
+
+ // FIXME : should use bios=cmos|auto|disable bits
+ // FIXME : should know about translation bits
+ // FIXME : move hard_drive_post here
+
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : software reset
+// ---------------------------------------------------------------------------
+// ATA-3
+// 8.2.1 Software reset - Device 0
+
+void ata_reset(device)
+Bit16u device;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit16u iobase1, iobase2;
+ Bit8u channel, slave, sn, sc;
+ Bit8u type;
+ Bit16u max;
+
+ channel = device / 2;
+ slave = device % 2;
+
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+ iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+
+ // Reset
+
+// 8.2.1 (a) -- set SRST in DC
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
+
+// 8.2.1 (b) -- wait for BSY
+ await_ide(BSY, iobase1, 20);
+
+// 8.2.1 (f) -- clear SRST
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+ type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
+ if (type != ATA_TYPE_NONE) {
+
+// 8.2.1 (g) -- check for sc==sn==0x01
+ // select device
+ outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
+ sc = inb(iobase1+ATA_CB_SC);
+ sn = inb(iobase1+ATA_CB_SN);
+
+ if ( (sc==0x01) && (sn==0x01) ) {
+ if (type == ATA_TYPE_ATA) //ATA
+ await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
+ else //ATAPI
+ await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+ }
+
+// 8.2.1 (h) -- wait for not BSY
+ await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+ }
+
+ // Enable interrupts
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a non data command
+// ---------------------------------------------------------------------------
+
+Bit16u ata_cmd_non_data()
+{return 0;}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a data-in command
+// ---------------------------------------------------------------------------
+ // returns
+ // 0 : no error
+ // 1 : BUSY bit set
+ // 2 : read error
+ // 3 : expected DRQ=1
+ // 4 : no sectors left to read/verify
+ // 5 : more sectors to read/verify
+ // 6 : no sectors left to write
+ // 7 : more sectors to write
+Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
+Bit16u device, command, count, cylinder, head, sector, segment, offset;
+Bit32u lba_low, lba_high;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit16u iobase1, iobase2, blksize;
+ Bit8u channel, slave;
+ Bit8u status, current, mode;
+
+ channel = device / 2;
+ slave = device % 2;
+
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+ iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+ mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+ blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+ if (mode == ATA_MODE_PIO32) blksize>>=2;
+ else blksize>>=1;
+
+ // Reset count of transferred data
+ write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
+ write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
+ current = 0;
+
+ status = inb(iobase1 + ATA_CB_STAT);
+ if (status & ATA_CB_STAT_BSY) return 1;
+
+ outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+ // sector will be 0 only on lba access. Convert to lba-chs
+ if (sector == 0) {
+ if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
+ outb(iobase1 + ATA_CB_FR, 0x00);
+ outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
+ outb(iobase1 + ATA_CB_SN, lba_low >> 24);
+ outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
+ outb(iobase1 + ATA_CB_CH, lba_high >> 8);
+ command |= 0x04;
+ count &= (1UL << 8) - 1;
+ lba_low &= (1UL << 24) - 1;
+ }
+ sector = (Bit16u) (lba_low & 0x000000ffL);
+ cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
+ head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
+ }
+
+ outb(iobase1 + ATA_CB_FR, 0x00);
+ outb(iobase1 + ATA_CB_SC, count);
+ outb(iobase1 + ATA_CB_SN, sector);
+ outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
+ outb(iobase1 + ATA_CB_CH, cylinder >> 8);
+ outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
+ outb(iobase1 + ATA_CB_CMD, command);
+
+ await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+ status = inb(iobase1 + ATA_CB_STAT);
+
+ if (status & ATA_CB_STAT_ERR) {
+ BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
+ return 2;
+ } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+ BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
+ return 3;
+ }
+
+ // FIXME : move seg/off translation here
+
+ASM_START
+ sti ;; enable higher priority interrupts
+ASM_END
+
+ while (1) {
+
+ASM_START
+ push bp
+ mov bp, sp
+ mov di, _ata_cmd_data_in.offset + 2[bp]
+ mov ax, _ata_cmd_data_in.segment + 2[bp]
+ mov cx, _ata_cmd_data_in.blksize + 2[bp]
+
+ ;; adjust if there will be an overrun. 2K max sector size
+ cmp di, #0xf800 ;;
+ jbe ata_in_no_adjust
+
+ata_in_adjust:
+ sub di, #0x0800 ;; sub 2 kbytes from offset
+ add ax, #0x0080 ;; add 2 Kbytes to segment
+
+ata_in_no_adjust:
+ mov es, ax ;; segment in es
+
+ mov dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
+
+ mov ah, _ata_cmd_data_in.mode + 2[bp]
+ cmp ah, #ATA_MODE_PIO32
+ je ata_in_32
+
+ata_in_16:
+ rep
+ insw ;; CX words transfered from port(DX) to ES:[DI]
+ jmp ata_in_done
+
+ata_in_32:
+ rep
+ insd ;; CX dwords transfered from port(DX) to ES:[DI]
+
+ata_in_done:
+ mov _ata_cmd_data_in.offset + 2[bp], di
+ mov _ata_cmd_data_in.segment + 2[bp], es
+ pop bp
+ASM_END
+
+ current++;
+ write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
+ count--;
+ await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+ status = inb(iobase1 + ATA_CB_STAT);
+ if (count == 0) {
+ if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+ != ATA_CB_STAT_RDY ) {
+ BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
+ return 4;
+ }
+ break;
+ }
+ else {
+ if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+ != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+ BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
+ return 5;
+ }
+ continue;
+ }
+ }
+ // Enable interrupts
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a data-out command
+// ---------------------------------------------------------------------------
+ // returns
+ // 0 : no error
+ // 1 : BUSY bit set
+ // 2 : read error
+ // 3 : expected DRQ=1
+ // 4 : no sectors left to read/verify
+ // 5 : more sectors to read/verify
+ // 6 : no sectors left to write
+ // 7 : more sectors to write
+Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba_low, lba_high, segment, offset)
+Bit16u device, command, count, cylinder, head, sector, segment, offset;
+Bit32u lba_low, lba_high;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit16u iobase1, iobase2, blksize;
+ Bit8u channel, slave;
+ Bit8u status, current, mode;
+
+ channel = device / 2;
+ slave = device % 2;
+
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+ iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+ mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+ blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+ if (mode == ATA_MODE_PIO32) blksize>>=2;
+ else blksize>>=1;
+
+ // Reset count of transferred data
+ write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
+ write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
+ current = 0;
+
+ status = inb(iobase1 + ATA_CB_STAT);
+ if (status & ATA_CB_STAT_BSY) return 1;
+
+ outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+ // sector will be 0 only on lba access. Convert to lba-chs
+ if (sector == 0) {
+ if ((count >= 1 << 8) || lba_high || (lba_low + count >= 1UL << 28)) {
+ outb(iobase1 + ATA_CB_FR, 0x00);
+ outb(iobase1 + ATA_CB_SC, (count >> 8) & 0xff);
+ outb(iobase1 + ATA_CB_SN, lba_low >> 24);
+ outb(iobase1 + ATA_CB_CL, lba_high & 0xff);
+ outb(iobase1 + ATA_CB_CH, lba_high >> 8);
+ command |= 0x04;
+ count &= (1UL << 8) - 1;
+ lba_low &= (1UL << 24) - 1;
+ }
+ sector = (Bit16u) (lba_low & 0x000000ffL);
+ cylinder = (Bit16u) ((lba_low>>8) & 0x0000ffffL);
+ head = ((Bit16u) ((lba_low>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
+ }
+
+ outb(iobase1 + ATA_CB_FR, 0x00);
+ outb(iobase1 + ATA_CB_SC, count);
+ outb(iobase1 + ATA_CB_SN, sector);
+ outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
+ outb(iobase1 + ATA_CB_CH, cylinder >> 8);
+ outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
+ outb(iobase1 + ATA_CB_CMD, command);
+
+ await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+ status = inb(iobase1 + ATA_CB_STAT);
+
+ if (status & ATA_CB_STAT_ERR) {
+ BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
+ return 2;
+ } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+ BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
+ return 3;
+ }
+
+ // FIXME : move seg/off translation here
+
+ASM_START
+ sti ;; enable higher priority interrupts
+ASM_END
+
+ while (1) {
+
+ASM_START
+ push bp
+ mov bp, sp
+ mov si, _ata_cmd_data_out.offset + 2[bp]
+ mov ax, _ata_cmd_data_out.segment + 2[bp]
+ mov cx, _ata_cmd_data_out.blksize + 2[bp]
+
+ ;; adjust if there will be an overrun. 2K max sector size
+ cmp si, #0xf800 ;;
+ jbe ata_out_no_adjust
+
+ata_out_adjust:
+ sub si, #0x0800 ;; sub 2 kbytes from offset
+ add ax, #0x0080 ;; add 2 Kbytes to segment
+
+ata_out_no_adjust:
+ mov es, ax ;; segment in es
+
+ mov dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
+
+ mov ah, _ata_cmd_data_out.mode + 2[bp]
+ cmp ah, #ATA_MODE_PIO32
+ je ata_out_32
+
+ata_out_16:
+ seg ES
+ rep
+ outsw ;; CX words transfered from port(DX) to ES:[SI]
+ jmp ata_out_done
+
+ata_out_32:
+ seg ES
+ rep
+ outsd ;; CX dwords transfered from port(DX) to ES:[SI]
+
+ata_out_done:
+ mov _ata_cmd_data_out.offset + 2[bp], si
+ mov _ata_cmd_data_out.segment + 2[bp], es
+ pop bp
+ASM_END
+
+ current++;
+ write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
+ count--;
+ status = inb(iobase1 + ATA_CB_STAT);
+ if (count == 0) {
+ if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+ != ATA_CB_STAT_RDY ) {
+ BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
+ return 6;
+ }
+ break;
+ }
+ else {
+ if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+ != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+ BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
+ return 7;
+ }
+ continue;
+ }
+ }
+ // Enable interrupts
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a packet command
+// ---------------------------------------------------------------------------
+ // returns
+ // 0 : no error
+ // 1 : error in parameters
+ // 2 : BUSY bit set
+ // 3 : error
+ // 4 : not ready
+Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
+Bit8u cmdlen,inout;
+Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
+Bit16u header;
+Bit32u length;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit16u iobase1, iobase2;
+ Bit16u lcount, lbefore, lafter, count;
+ Bit8u channel, slave;
+ Bit8u status, mode, lmode;
+ Bit32u total, transfer;
+
+ channel = device / 2;
+ slave = device % 2;
+
+ // Data out is not supported yet
+ if (inout == ATA_DATA_OUT) {
+ BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
+ return 1;
+ }
+
+ // The header length must be even
+ if (header & 1) {
+ BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
+ return 1;
+ }
+
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+ iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+ mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+ transfer= 0L;
+
+ if (cmdlen < 12) cmdlen=12;
+ if (cmdlen > 12) cmdlen=16;
+ cmdlen>>=1;
+
+ // Reset count of transferred data
+ write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
+ write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
+
+ status = inb(iobase1 + ATA_CB_STAT);
+ if (status & ATA_CB_STAT_BSY) return 2;
+
+ outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+ outb(iobase1 + ATA_CB_FR, 0x00);
+ outb(iobase1 + ATA_CB_SC, 0x00);
+ outb(iobase1 + ATA_CB_SN, 0x00);
+ outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
+ outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
+ outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
+ outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
+
+ // Device should ok to receive command
+ await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+ status = inb(iobase1 + ATA_CB_STAT);
+
+ if (status & ATA_CB_STAT_ERR) {
+ BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
+ return 3;
+ } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+ BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
+ return 4;
+ }
+
+ // Normalize address
+ cmdseg += (cmdoff / 16);
+ cmdoff %= 16;
+
+ // Send command to device
+ASM_START
+ sti ;; enable higher priority interrupts
+
+ push bp
+ mov bp, sp
+
+ mov si, _ata_cmd_packet.cmdoff + 2[bp]
+ mov ax, _ata_cmd_packet.cmdseg + 2[bp]
+ mov cx, _ata_cmd_packet.cmdlen + 2[bp]
+ mov es, ax ;; segment in es
+
+ mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
+
+ seg ES
+ rep
+ outsw ;; CX words transfered from port(DX) to ES:[SI]
+
+ pop bp
+ASM_END
+
+ if (inout == ATA_DATA_NO) {
+ await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+ status = inb(iobase1 + ATA_CB_STAT);
+ }
+ else {
+ Bit16u loops = 0;
+ Bit8u sc;
+ while (1) {
+
+ if (loops == 0) {//first time through
+ status = inb(iobase2 + ATA_CB_ASTAT);
+ await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+ }
+ else
+ await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+ loops++;
+
+ status = inb(iobase1 + ATA_CB_STAT);
+ sc = inb(iobase1 + ATA_CB_SC);
+
+ // Check if command completed
+ if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
+ ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY)) break;
+
+ if (status & ATA_CB_STAT_ERR) {
+ BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
+ return 3;
+ }
+
+ // Normalize address
+ bufseg += (bufoff / 16);
+ bufoff %= 16;
+
+ // Get the byte count
+ lcount = ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
+
+ // adjust to read what we want
+ if(header>lcount) {
+ lbefore=lcount;
+ header-=lcount;
+ lcount=0;
+ }
+ else {
+ lbefore=header;
+ header=0;
+ lcount-=lbefore;
+ }
+
+ if(lcount>length) {
+ lafter=lcount-length;
+ lcount=length;
+ length=0;
+ }
+ else {
+ lafter=0;
+ length-=lcount;
+ }
+
+ // Save byte count
+ count = lcount;
+
+ BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
+ BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
+
+ // If counts not dividable by 4, use 16bits mode
+ lmode = mode;
+ if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
+ if (lcount & 0x03) lmode=ATA_MODE_PIO16;
+ if (lafter & 0x03) lmode=ATA_MODE_PIO16;
+
+ // adds an extra byte if count are odd. before is always even
+ if (lcount & 0x01) {
+ lcount+=1;
+ if ((lafter > 0) && (lafter & 0x01)) {
+ lafter-=1;
+ }
+ }
+
+ if (lmode == ATA_MODE_PIO32) {
+ lcount>>=2; lbefore>>=2; lafter>>=2;
+ }
+ else {
+ lcount>>=1; lbefore>>=1; lafter>>=1;
+ }
+
+ ; // FIXME bcc bug
+
+ASM_START
+ push bp
+ mov bp, sp
+
+ mov dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
+
+ mov cx, _ata_cmd_packet.lbefore + 2[bp]
+ jcxz ata_packet_no_before
+
+ mov ah, _ata_cmd_packet.lmode + 2[bp]
+ cmp ah, #ATA_MODE_PIO32
+ je ata_packet_in_before_32
+
+ata_packet_in_before_16:
+ in ax, dx
+ loop ata_packet_in_before_16
+ jmp ata_packet_no_before
+
+ata_packet_in_before_32:
+ push eax
+ata_packet_in_before_32_loop:
+ in eax, dx
+ loop ata_packet_in_before_32_loop
+ pop eax
+
+ata_packet_no_before:
+ mov cx, _ata_cmd_packet.lcount + 2[bp]
+ jcxz ata_packet_after
+
+ mov di, _ata_cmd_packet.bufoff + 2[bp]
+ mov ax, _ata_cmd_packet.bufseg + 2[bp]
+ mov es, ax
+
+ mov ah, _ata_cmd_packet.lmode + 2[bp]
+ cmp ah, #ATA_MODE_PIO32
+ je ata_packet_in_32
+
+ata_packet_in_16:
+ rep
+ insw ;; CX words transfered tp port(DX) to ES:[DI]
+ jmp ata_packet_after
+
+ata_packet_in_32:
+ rep
+ insd ;; CX dwords transfered to port(DX) to ES:[DI]
+
+ata_packet_after:
+ mov cx, _ata_cmd_packet.lafter + 2[bp]
+ jcxz ata_packet_done
+
+ mov ah, _ata_cmd_packet.lmode + 2[bp]
+ cmp ah, #ATA_MODE_PIO32
+ je ata_packet_in_after_32
+
+ata_packet_in_after_16:
+ in ax, dx
+ loop ata_packet_in_after_16
+ jmp ata_packet_done
+
+ata_packet_in_after_32:
+ push eax
+ata_packet_in_after_32_loop:
+ in eax, dx
+ loop ata_packet_in_after_32_loop
+ pop eax
+
+ata_packet_done:
+ pop bp
+ASM_END
+
+ // Compute new buffer address
+ bufoff += count;
+
+ // Save transferred bytes count
+ transfer += count;
+ write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
+ }
+ }
+
+ // Final check, device must be ready
+ if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+ != ATA_CB_STAT_RDY ) {
+ BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
+ return 4;
+ }
+
+ // Enable interrupts
+ outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+// End of ATA/ATAPI Driver
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Start of ATA/ATAPI generic functions
+// ---------------------------------------------------------------------------
+
+ Bit16u
+atapi_get_sense(device, seg, asc, ascq)
+ Bit16u device;
+{
+ Bit8u atacmd[12];
+ Bit8u buffer[18];
+ Bit8u i;
+
+ memsetb(get_SS(),atacmd,0,12);
+
+ // Request SENSE
+ atacmd[0]=ATA_CMD_REQUEST_SENSE;
+ atacmd[4]=sizeof(buffer);
+ if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 18L, ATA_DATA_IN, get_SS(), buffer) != 0)
+ return 0x0002;
+
+ write_byte(seg,asc,buffer[12]);
+ write_byte(seg,ascq,buffer[13]);
+
+ return 0;
+}
+
+ Bit16u
+atapi_is_ready(device)
+ Bit16u device;
+{
+ Bit8u packet[12];
+ Bit8u buf[8];
+ Bit32u block_len;
+ Bit32u sectors;
+ Bit32u timeout; //measured in ms
+ Bit32u time;
+ Bit8u asc, ascq;
+ Bit8u in_progress;
+ Bit16u ebda_seg = read_word(0x0040,0x000E);
+ if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI) {
+ printf("not implemented for non-ATAPI device\n");
+ return -1;
+ }
+
+ BX_DEBUG_ATA("ata_detect_medium: begin\n");
+ memsetb(get_SS(),packet, 0, sizeof packet);
+ packet[0] = 0x25; /* READ CAPACITY */
+
+ /* Retry READ CAPACITY 50 times unless MEDIUM NOT PRESENT
+ * is reported by the device. If the device reports "IN PROGRESS",
+ * 30 seconds is added. */
+ timeout = 5000;
+ time = 0;
+ in_progress = 0;
+ while (time < timeout) {
+ if (ata_cmd_packet(device, sizeof(packet), get_SS(), packet, 0, 8L, ATA_DATA_IN, get_SS(), buf) == 0)
+ goto ok;
+
+ if (atapi_get_sense(device, get_SS(), &asc, &ascq) == 0) {
+ if (asc == 0x3a) { /* MEDIUM NOT PRESENT */
+ BX_DEBUG_ATA("Device reports MEDIUM NOT PRESENT\n");
+ return -1;
+ }
+
+ if (asc == 0x04 && ascq == 0x01 && !in_progress) {
+ /* IN PROGRESS OF BECOMING READY */
+ printf("Waiting for device to detect medium... ");
+ /* Allow 30 seconds more */
+ timeout = 30000;
+ in_progress = 1;
+ }
+ }
+ time += 100;
+ }
+ BX_DEBUG_ATA("read capacity failed\n");
+ return -1;
+ok:
+
+ block_len = (Bit32u) buf[4] << 24
+ | (Bit32u) buf[5] << 16
+ | (Bit32u) buf[6] << 8
+ | (Bit32u) buf[7] << 0;
+ BX_DEBUG_ATA("block_len=%u\n", block_len);
+
+ if (block_len!= 2048 && block_len!= 512)
+ {
+ printf("Unsupported sector size %u\n", block_len);
+ return -1;
+ }
+ write_dword(ebda_seg,&EbdaData->ata.devices[device].blksize, block_len);
+
+ sectors = (Bit32u) buf[0] << 24
+ | (Bit32u) buf[1] << 16
+ | (Bit32u) buf[2] << 8
+ | (Bit32u) buf[3] << 0;
+
+ BX_DEBUG_ATA("sectors=%u\n", sectors);
+ if (block_len == 2048)
+ sectors <<= 2; /* # of sectors in 512-byte "soft" sector */
+ if (sectors != read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low))
+ printf("%dMB medium detected\n", sectors>>(20-9));
+ write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors);
+ return 0;
+}
+
+ Bit16u
+atapi_is_cdrom(device)
+ Bit8u device;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+ if (device >= BX_MAX_ATA_DEVICES)
+ return 0;
+
+ if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
+ return 0;
+
+ if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
+ return 0;
+
+ return 1;
+}
+
+// ---------------------------------------------------------------------------
+// End of ATA/ATAPI generic functions
+// ---------------------------------------------------------------------------
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+
+// ---------------------------------------------------------------------------
+// Start of El-Torito boot functions
+// ---------------------------------------------------------------------------
+
+ void
+cdemu_init()
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+ // the only important data is this one for now
+ write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
+}
+
+ Bit8u
+cdemu_isactive()
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+ return(read_byte(ebda_seg,&EbdaData->cdemu.active));
+}
+
+ Bit8u
+cdemu_emulated_drive()
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+ return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
+}
+
+static char isotag[6]="CD001";
+static char eltorito[24]="EL TORITO SPECIFICATION";
+//
+// Returns ah: emulated drive, al: error code
+//
+ Bit16u
+cdrom_boot()
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit8u atacmd[12], buffer[2048];
+ Bit32u lba;
+ Bit16u boot_segment, nbsectors, i, error;
+ Bit8u device;
+
+ // Find out the first cdrom
+ for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
+ if (atapi_is_cdrom(device)) break;
+ }
+
+ // if not found
+ if(device >= BX_MAX_ATA_DEVICES) return 2;
+
+ if(error = atapi_is_ready(device) != 0)
+ BX_INFO("ata_is_ready returned %d\n",error);
+
+ // Read the Boot Record Volume Descriptor
+ memsetb(get_SS(),atacmd,0,12);
+ atacmd[0]=0x28; // READ command
+ atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
+ atacmd[8]=(0x01 & 0x00ff); // Sectors
+ atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
+ atacmd[3]=(0x11 & 0x00ff0000) >> 16;
+ atacmd[4]=(0x11 & 0x0000ff00) >> 8;
+ atacmd[5]=(0x11 & 0x000000ff);
+ if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
+ return 3;
+
+ // Validity checks
+ if(buffer[0]!=0)return 4;
+ for(i=0;i<5;i++){
+ if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
+ }
+ for(i=0;i<23;i++)
+ if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
+
+ // ok, now we calculate the Boot catalog address
+ lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
+
+ // And we read the Boot Catalog
+ memsetb(get_SS(),atacmd,0,12);
+ atacmd[0]=0x28; // READ command
+ atacmd[7]=(0x01 & 0xff00) >> 8; // Sectors
+ atacmd[8]=(0x01 & 0x00ff); // Sectors
+ atacmd[2]=(lba & 0xff000000) >> 24; // LBA
+ atacmd[3]=(lba & 0x00ff0000) >> 16;
+ atacmd[4]=(lba & 0x0000ff00) >> 8;
+ atacmd[5]=(lba & 0x000000ff);
+ if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
+ return 7;
+
+ // Validation entry
+ if(buffer[0x00]!=0x01)return 8; // Header
+ if(buffer[0x01]!=0x00)return 9; // Platform
+ if(buffer[0x1E]!=0x55)return 10; // key 1
+ if(buffer[0x1F]!=0xAA)return 10; // key 2
+
+ // Initial/Default Entry
+ if(buffer[0x20]!=0x88)return 11; // Bootable
+
+ write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
+ if(buffer[0x21]==0){
+ // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0.
+ // Win2000 cd boot needs to know it booted from cd
+ write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
+ }
+ else if(buffer[0x21]<4)
+ write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
+ else
+ write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
+
+ write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
+ write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
+
+ boot_segment=buffer[0x23]*0x100+buffer[0x22];
+ if(boot_segment==0x0000)boot_segment=0x07C0;
+
+ write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
+ write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
+
+ nbsectors=buffer[0x27]*0x100+buffer[0x26];
+ write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
+
+ lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
+ write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
+
+ // And we read the image in memory
+ memsetb(get_SS(),atacmd,0,12);
+ atacmd[0]=0x28; // READ command
+ atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8; // Sectors
+ atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff); // Sectors
+ atacmd[2]=(lba & 0xff000000) >> 24; // LBA
+ atacmd[3]=(lba & 0x00ff0000) >> 16;
+ atacmd[4]=(lba & 0x0000ff00) >> 8;
+ atacmd[5]=(lba & 0x000000ff);
+ if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
+ return 12;
+
+ // Remember the media type
+ switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
+ case 0x01: // 1.2M floppy
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
+ break;
+ case 0x02: // 1.44M floppy
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
+ break;
+ case 0x03: // 2.88M floppy
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
+ break;
+ case 0x04: // Harddrive
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
+ (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
+ write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
+ break;
+ }
+
+ if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
+ // Increase bios installed hardware number of devices
+ if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
+ write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
+ else
+ write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
+ }
+
+
+ // everything is ok, so from now on, the emulation is active
+ if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
+ write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
+
+ // return the boot drive + no error
+ return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
+}
+
+// ---------------------------------------------------------------------------
+// End of El-Torito boot functions
+// ---------------------------------------------------------------------------
+#endif // BX_ELTORITO_BOOT
+
+ void
+int14_function(regs, ds, iret_addr)
+ pusha_regs_t regs; // regs pushed from PUSHA instruction
+ Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+ iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
+{
+ Bit16u addr,timer,val16;
+ Bit8u timeout;
+
+ ASM_START
+ sti
+ ASM_END
+
+ addr = read_word(0x0040, (regs.u.r16.dx << 1));
+ timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
+ if ((regs.u.r16.dx < 4) && (addr > 0)) {
+ switch (regs.u.r8.ah) {
+ case 0:
+ outb(addr+3, inb(addr+3) | 0x80);
+ if (regs.u.r8.al & 0xE0 == 0) {
+ outb(addr, 0x17);
+ outb(addr+1, 0x04);
+ } else {
+ val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
+ outb(addr, val16 & 0xFF);
+ outb(addr+1, val16 >> 8);
+ }
+ outb(addr+3, regs.u.r8.al & 0x1F);
+ regs.u.r8.ah = inb(addr+5);
+ regs.u.r8.al = inb(addr+6);
+ ClearCF(iret_addr.flags);
+ break;
+ case 1:
+ timer = read_word(0x0040, 0x006C);
+ while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
+ val16 = read_word(0x0040, 0x006C);
+ if (val16 != timer) {
+ timer = val16;
+ timeout--;
+ }
+ }
+ if (timeout) outb(addr, regs.u.r8.al);
+ regs.u.r8.ah = inb(addr+5);
+ if (!timeout) regs.u.r8.ah |= 0x80;
+ ClearCF(iret_addr.flags);
+ break;
+ case 2:
+ timer = read_word(0x0040, 0x006C);
+ while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
+ val16 = read_word(0x0040, 0x006C);
+ if (val16 != timer) {
+ timer = val16;
+ timeout--;
+ }
+ }
+ if (timeout) {
+ regs.u.r8.ah = 0;
+ regs.u.r8.al = inb(addr);
+ } else {
+ regs.u.r8.ah = inb(addr+5);
+ }
+ ClearCF(iret_addr.flags);
+ break;
+ case 3:
+ regs.u.r8.ah = inb(addr+5);
+ regs.u.r8.al = inb(addr+6);
+ ClearCF(iret_addr.flags);
+ break;
+ default:
+ SetCF(iret_addr.flags); // Unsupported
+ }
+ } else {
+ SetCF(iret_addr.flags); // Unsupported
+ }
+}
+
+ void
+int15_function(regs, ES, DS, FLAGS)
+ pusha_regs_t regs; // REGS pushed via pusha
+ Bit16u ES, DS, FLAGS;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ bx_bool prev_a20_enable;
+ Bit16u base15_00;
+ Bit8u base23_16;
+ Bit16u ss;
+ Bit16u CX,DX;
+
+ Bit16u bRegister;
+ Bit8u irqDisable;
+
+BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
+
+ switch (regs.u.r8.ah) {
+ case 0x24: /* A20 Control */
+ switch (regs.u.r8.al) {
+ case 0x00:
+ set_enable_a20(0);
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ break;
+ case 0x01:
+ set_enable_a20(1);
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ break;
+ case 0x02:
+ regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ break;
+ case 0x03:
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ regs.u.r16.bx = 3;
+ break;
+ default:
+ BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ break;
+
+ case 0x41:
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ break;
+
+ case 0x4f:
+ /* keyboard intercept */
+#if BX_CPU < 2
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+#else
+ // nop
+#endif
+ SET_CF();
+ break;
+
+ case 0x52: // removable media eject
+ CLEAR_CF();
+ regs.u.r8.ah = 0; // "ok ejection may proceed"
+ break;
+
+ case 0x83: {
+ if( regs.u.r8.al == 0 ) {
+ // Set Interval requested.
+ if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
+ // Interval not already set.
+ write_byte( 0x40, 0xA0, 1 ); // Set status byte.
+ write_word( 0x40, 0x98, ES ); // Byte location, segment
+ write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
+ write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
+ write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
+ CLEAR_CF( );
+ irqDisable = inb( 0xA1 );
+ outb( 0xA1, irqDisable & 0xFE );
+ bRegister = inb_cmos( 0xB ); // Unmask IRQ8 so INT70 will get through.
+ outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
+ } else {
+ // Interval already set.
+ BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ } else if( regs.u.r8.al == 1 ) {
+ // Clear Interval requested
+ write_byte( 0x40, 0xA0, 0 ); // Clear status byte
+ CLEAR_CF( );
+ bRegister = inb_cmos( 0xB );
+ outb_cmos( 0xB, bRegister & ~0x40 ); // Turn off the Periodic Interrupt timer
+ } else {
+ BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ regs.u.r8.al--;
+ }
+
+ break;
+ }
+
+ case 0x87:
+#if BX_CPU < 3
+# error "Int15 function 87h not supported on < 80386"
+#endif
+ // +++ should probably have descriptor checks
+ // +++ should have exception handlers
+
+ // turn off interrupts
+ASM_START
+ cli
+ASM_END
+
+ prev_a20_enable = set_enable_a20(1); // enable A20 line
+
+ // 128K max of transfer on 386+ ???
+ // source == destination ???
+
+ // ES:SI points to descriptor table
+ // offset use initially comments
+ // ==============================================
+ // 00..07 Unused zeros Null descriptor
+ // 08..0f GDT zeros filled in by BIOS
+ // 10..17 source ssssssss source of data
+ // 18..1f dest dddddddd destination of data
+ // 20..27 CS zeros filled in by BIOS
+ // 28..2f SS zeros filled in by BIOS
+
+ //es:si
+ //eeee0
+ //0ssss
+ //-----
+
+// check for access rights of source & dest here
+
+ // Initialize GDT descriptor
+ base15_00 = (ES << 4) + regs.u.r16.si;
+ base23_16 = ES >> 12;
+ if (base15_00 < (ES<<4))
+ base23_16++;
+ write_word(ES, regs.u.r16.si+0x08+0, 47); // limit 15:00 = 6 * 8bytes/descriptor
+ write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
+ write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
+ write_byte(ES, regs.u.r16.si+0x08+5, 0x93); // access
+ write_word(ES, regs.u.r16.si+0x08+6, 0x0000); // base 31:24/reserved/limit 19:16
+
+ // Initialize CS descriptor
+ write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
+ write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
+ write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
+ write_byte(ES, regs.u.r16.si+0x20+5, 0x9b); // access
+ write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
+
+ // Initialize SS descriptor
+ ss = get_SS();
+ base15_00 = ss << 4;
+ base23_16 = ss >> 12;
+ write_word(ES, regs.u.r16.si+0x28+0, 0xffff); // limit 15:00 = normal 64K limit
+ write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
+ write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
+ write_byte(ES, regs.u.r16.si+0x28+5, 0x93); // access
+ write_word(ES, regs.u.r16.si+0x28+6, 0x0000); // base 31:24/reserved/limit 19:16
+
+ CX = regs.u.r16.cx;
+ASM_START
+ // Compile generates locals offset info relative to SP.
+ // Get CX (word count) from stack.
+ mov bx, sp
+ SEG SS
+ mov cx, _int15_function.CX [bx]
+
+ // since we need to set SS:SP, save them to the BDA
+ // for future restore
+ push eax
+ xor eax, eax
+ mov ds, ax
+ mov 0x0469, ss
+ mov 0x0467, sp
+
+ SEG ES
+ lgdt [si + 0x08]
+ SEG CS
+ lidt [pmode_IDT_info]
+ ;; perhaps do something with IDT here
+
+ ;; set PE bit in CR0
+ mov eax, cr0
+ or al, #0x01
+ mov cr0, eax
+ ;; far jump to flush CPU queue after transition to protected mode
+ JMP_AP(0x0020, protected_mode)
+
+protected_mode:
+ ;; GDT points to valid descriptor table, now load SS, DS, ES
+ mov ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
+ mov ss, ax
+ mov ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
+ mov ds, ax
+ mov ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
+ mov es, ax
+ xor si, si
+ xor di, di
+ cld
+ rep
+ movsw ;; move CX words from DS:SI to ES:DI
+
+ ;; make sure DS and ES limits are 64KB
+ mov ax, #0x28
+ mov ds, ax
+ mov es, ax
+
+ ;; reset PG bit in CR0 ???
+ mov eax, cr0
+ and al, #0xFE
+ mov cr0, eax
+
+ ;; far jump to flush CPU queue after transition to real mode
+ JMP_AP(0xf000, real_mode)
+
+real_mode:
+ ;; restore IDT to normal real-mode defaults
+ SEG CS
+ lidt [rmode_IDT_info]
+
+ // restore SS:SP from the BDA
+ xor ax, ax
+ mov ds, ax
+ mov ss, 0x0469
+ mov sp, 0x0467
+ pop eax
+ASM_END
+
+ set_enable_a20(prev_a20_enable);
+
+ // turn back on interrupts
+ASM_START
+ sti
+ASM_END
+
+ regs.u.r8.ah = 0;
+ CLEAR_CF();
+ break;
+
+
+ case 0x88:
+ // Get the amount of extended memory (above 1M)
+#if BX_CPU < 2
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ SET_CF();
+#else
+ regs.u.r8.al = inb_cmos(0x30);
+ regs.u.r8.ah = inb_cmos(0x31);
+
+ // According to Ralf Brown's interrupt the limit should be 15M,
+ // but real machines mostly return max. 63M.
+ if(regs.u.r16.ax > 0xffc0)
+ regs.u.r16.ax = 0xffc0;
+
+ CLEAR_CF();
+#endif
+ break;
+
+ case 0x90:
+ /* Device busy interrupt. Called by Int 16h when no key available */
+ break;
+
+ case 0x91:
+ /* Interrupt complete. Called by Int 16h when key becomes available */
+ break;
+
+ case 0xbf:
+ BX_INFO("*** int 15h function AH=bf not yet supported!\n");
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ break;
+
+ case 0xC0:
+#if 0
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ break;
+#endif
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ regs.u.r16.bx = BIOS_CONFIG_TABLE;
+ ES = 0xF000;
+ break;
+
+ case 0xc1:
+ ES = ebda_seg;
+ CLEAR_CF();
+ break;
+
+ case 0xd8:
+ bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ break;
+
+ default:
+ BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
+ (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ break;
+ }
+}
+
+#if BX_USE_PS2_MOUSE
+ void
+int15_function_mouse(regs, ES, DS, FLAGS)
+ pusha_regs_t regs; // REGS pushed via pusha
+ Bit16u ES, DS, FLAGS;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit8u mouse_flags_1, mouse_flags_2;
+ Bit16u mouse_driver_seg;
+ Bit16u mouse_driver_offset;
+ Bit8u comm_byte, prev_command_byte;
+ Bit8u ret, mouse_data1, mouse_data2, mouse_data3;
+
+BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
+
+ switch (regs.u.r8.ah) {
+ case 0xC2:
+ // Return Codes status in AH
+ // =========================
+ // 00: success
+ // 01: invalid subfunction (AL > 7)
+ // 02: invalid input value (out of allowable range)
+ // 03: interface error
+ // 04: resend command received from mouse controller,
+ // device driver should attempt command again
+ // 05: cannot enable mouse, since no far call has been installed
+ // 80/86: mouse service not implemented
+
+ switch (regs.u.r8.al) {
+ case 0: // Disable/Enable Mouse
+BX_DEBUG_INT15("case 0:\n");
+ switch (regs.u.r8.bh) {
+ case 0: // Disable Mouse
+BX_DEBUG_INT15("case 0: disable mouse\n");
+ inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ ret = send_to_mouse_ctrl(0xF5); // disable mouse command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data1);
+ if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ return;
+ }
+ }
+
+ // error
+ SET_CF();
+ regs.u.r8.ah = ret;
+ return;
+ break;
+
+ case 1: // Enable Mouse
+BX_DEBUG_INT15("case 1: enable mouse\n");
+ mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+ if ( (mouse_flags_2 & 0x80) == 0 ) {
+ BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
+ SET_CF(); // error
+ regs.u.r8.ah = 5; // no far call installed
+ return;
+ }
+ inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ ret = send_to_mouse_ctrl(0xF4); // enable mouse command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data1);
+ if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
+ enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ return;
+ }
+ }
+ SET_CF();
+ regs.u.r8.ah = ret;
+ return;
+
+ default: // invalid subfunction
+ BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
+ SET_CF(); // error
+ regs.u.r8.ah = 1; // invalid subfunction
+ return;
+ }
+ break;
+
+ case 1: // Reset Mouse
+ case 5: // Initialize Mouse
+BX_DEBUG_INT15("case 1 or 5:\n");
+ if (regs.u.r8.al == 5) {
+ if (regs.u.r8.bh != 3) {
+ SET_CF();
+ regs.u.r8.ah = 0x02; // invalid input
+ return;
+ }
+ mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+ mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
+ mouse_flags_1 = 0x00;
+ write_byte(ebda_seg, 0x0026, mouse_flags_1);
+ write_byte(ebda_seg, 0x0027, mouse_flags_2);
+ }
+
+ inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ ret = send_to_mouse_ctrl(0xFF); // reset mouse command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data3);
+ // if no mouse attached, it will return RESEND
+ if (mouse_data3 == 0xfe) {
+ SET_CF();
+ return;
+ }
+ if (mouse_data3 != 0xfa)
+ BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
+ if ( ret == 0 ) {
+ ret = get_mouse_data(&mouse_data1);
+ if ( ret == 0 ) {
+ ret = get_mouse_data(&mouse_data2);
+ if ( ret == 0 ) {
+ // turn IRQ12 and packet generation on
+ enable_mouse_int_and_events();
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ regs.u.r8.bl = mouse_data1;
+ regs.u.r8.bh = mouse_data2;
+ return;
+ }
+ }
+ }
+ }
+
+ // error
+ SET_CF();
+ regs.u.r8.ah = ret;
+ return;
+
+ case 2: // Set Sample Rate
+BX_DEBUG_INT15("case 2:\n");
+ switch (regs.u.r8.bh) {
+ case 0: mouse_data1 = 10; break; // 10 reports/sec
+ case 1: mouse_data1 = 20; break; // 20 reports/sec
+ case 2: mouse_data1 = 40; break; // 40 reports/sec
+ case 3: mouse_data1 = 60; break; // 60 reports/sec
+ case 4: mouse_data1 = 80; break; // 80 reports/sec
+ case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
+ case 6: mouse_data1 = 200; break; // 200 reports/sec
+ default: mouse_data1 = 0;
+ }
+ if (mouse_data1 > 0) {
+ ret = send_to_mouse_ctrl(0xF3); // set sample rate command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data2);
+ ret = send_to_mouse_ctrl(mouse_data1);
+ ret = get_mouse_data(&mouse_data2);
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ } else {
+ // error
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ } else {
+ // error
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ break;
+
+ case 3: // Set Resolution
+BX_DEBUG_INT15("case 3:\n");
+ // BH:
+ // 0 = 25 dpi, 1 count per millimeter
+ // 1 = 50 dpi, 2 counts per millimeter
+ // 2 = 100 dpi, 4 counts per millimeter
+ // 3 = 200 dpi, 8 counts per millimeter
+ comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ if (regs.u.r8.bh < 4) {
+ ret = send_to_mouse_ctrl(0xE8); // set resolution command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data1);
+ if (mouse_data1 != 0xfa)
+ BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
+ ret = send_to_mouse_ctrl(regs.u.r8.bh);
+ ret = get_mouse_data(&mouse_data1);
+ if (mouse_data1 != 0xfa)
+ BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ } else {
+ // error
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ } else {
+ // error
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+ break;
+
+ case 4: // Get Device ID
+BX_DEBUG_INT15("case 4:\n");
+ inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data1);
+ ret = get_mouse_data(&mouse_data2);
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ regs.u.r8.bh = mouse_data2;
+ } else {
+ // error
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ break;
+
+ case 6: // Return Status & Set Scaling Factor...
+BX_DEBUG_INT15("case 6:\n");
+ switch (regs.u.r8.bh) {
+ case 0: // Return Status
+ comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ ret = send_to_mouse_ctrl(0xE9); // get mouse info command
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data1);
+ if (mouse_data1 != 0xfa)
+ BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
+ if (ret == 0) {
+ ret = get_mouse_data(&mouse_data1);
+ if ( ret == 0 ) {
+ ret = get_mouse_data(&mouse_data2);
+ if ( ret == 0 ) {
+ ret = get_mouse_data(&mouse_data3);
+ if ( ret == 0 ) {
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ regs.u.r8.bl = mouse_data1;
+ regs.u.r8.cl = mouse_data2;
+ regs.u.r8.dl = mouse_data3;
+ set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // error
+ SET_CF();
+ regs.u.r8.ah = ret;
+ set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+ return;
+
+ case 1: // Set Scaling Factor to 1:1
+ case 2: // Set Scaling Factor to 2:1
+ comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ if (regs.u.r8.bh == 1) {
+ ret = send_to_mouse_ctrl(0xE6);
+ } else {
+ ret = send_to_mouse_ctrl(0xE7);
+ }
+ if (ret == 0) {
+ get_mouse_data(&mouse_data1);
+ ret = (mouse_data1 != 0xFA);
+ }
+ if (ret == 0) {
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ } else {
+ // error
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ }
+ set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+ break;
+
+ default:
+ BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
+ }
+ break;
+
+ case 7: // Set Mouse Handler Address
+BX_DEBUG_INT15("case 7:\n");
+ mouse_driver_seg = ES;
+ mouse_driver_offset = regs.u.r16.bx;
+ write_word(ebda_seg, 0x0022, mouse_driver_offset);
+ write_word(ebda_seg, 0x0024, mouse_driver_seg);
+ mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+ if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
+ /* remove handler */
+ if ( (mouse_flags_2 & 0x80) != 0 ) {
+ mouse_flags_2 &= ~0x80;
+ inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+ }
+ }
+ else {
+ /* install handler */
+ mouse_flags_2 |= 0x80;
+ }
+ write_byte(ebda_seg, 0x0027, mouse_flags_2);
+ CLEAR_CF();
+ regs.u.r8.ah = 0;
+ break;
+
+ default:
+BX_DEBUG_INT15("case default:\n");
+ regs.u.r8.ah = 1; // invalid function
+ SET_CF();
+ }
+ break;
+
+ default:
+ BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
+ (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
+ SET_CF();
+ regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+ break;
+ }
+}
+#endif // BX_USE_PS2_MOUSE
+
+
+void set_e820_range(ES, DI, start, end, extra_start, extra_end, type)
+ Bit16u ES;
+ Bit16u DI;
+ Bit32u start;
+ Bit32u end;
+ Bit8u extra_start;
+ Bit8u extra_end;
+ Bit16u type;
+{
+ write_word(ES, DI, start);
+ write_word(ES, DI+2, start >> 16);
+ write_word(ES, DI+4, extra_start);
+ write_word(ES, DI+6, 0x00);
+
+ end -= start;
+ extra_end -= extra_start;
+ write_word(ES, DI+8, end);
+ write_word(ES, DI+10, end >> 16);
+ write_word(ES, DI+12, extra_end);
+ write_word(ES, DI+14, 0x0000);
+
+ write_word(ES, DI+16, type);
+ write_word(ES, DI+18, 0x0);
+}
+
+ void
+int15_function32(regs, ES, DS, FLAGS)
+ pushad_regs_t regs; // REGS pushed via pushad
+ Bit16u ES, DS, FLAGS;
+{
+ Bit32u extended_memory_size=0; // 64bits long
+ Bit32u extra_lowbits_memory_size=0;
+ Bit16u CX,DX;
+ Bit8u extra_highbits_memory_size=0;
+
+BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
+
+ switch (regs.u.r8.ah) {
+ case 0x86:
+ // Wait for CX:DX microseconds. currently using the
+ // refresh request port 0x61 bit4, toggling every 15usec
+
+ CX = regs.u.r16.cx;
+ DX = regs.u.r16.dx;
+
+ASM_START
+ sti
+
+ ;; Get the count in eax
+ mov bx, sp
+ SEG SS
+ mov ax, _int15_function32.CX [bx]
+ shl eax, #16
+ SEG SS
+ mov ax, _int15_function32.DX [bx]
+
+ ;; convert to numbers of 15usec ticks
+ mov ebx, #15
+ xor edx, edx
+ div eax, ebx
+ mov ecx, eax
+
+ ;; wait for ecx number of refresh requests
+ in al, #0x61
+ and al,#0x10
+ mov ah, al
+
+ or ecx, ecx
+ je int1586_tick_end
+int1586_tick:
+ in al, #0x61
+ and al,#0x10
+ cmp al, ah
+ je int1586_tick
+ mov ah, al
+ dec ecx
+ jnz int1586_tick
+int1586_tick_end:
+ASM_END
+
+ break;
+
+ case 0xe8:
+ switch(regs.u.r8.al)
+ {
+ case 0x20: // coded by osmaker aka K.J.
+ if(regs.u.r32.edx == 0x534D4150)
+ {
+ extended_memory_size = inb_cmos(0x35);
+ extended_memory_size <<= 8;
+ extended_memory_size |= inb_cmos(0x34);
+ extended_memory_size *= 64;
+ // greater than EFF00000???
+ if(extended_memory_size > 0x3bc000) {
+ extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
+ }
+ extended_memory_size *= 1024;
+ extended_memory_size += (16L * 1024 * 1024);
+
+ if(extended_memory_size <= (16L * 1024 * 1024)) {
+ extended_memory_size = inb_cmos(0x31);
+ extended_memory_size <<= 8;
+ extended_memory_size |= inb_cmos(0x30);
+ extended_memory_size *= 1024;
+ extended_memory_size += (1L * 1024 * 1024);
+ }
+
+ extra_lowbits_memory_size = inb_cmos(0x5c);
+ extra_lowbits_memory_size <<= 8;
+ extra_lowbits_memory_size |= inb_cmos(0x5b);
+ extra_lowbits_memory_size *= 64;
+ extra_lowbits_memory_size *= 1024;
+ extra_highbits_memory_size = inb_cmos(0x5d);
+
+ switch(regs.u.r16.bx)
+ {
+ case 0:
+ set_e820_range(ES, regs.u.r16.di,
+ 0x0000000L, 0x0009f000L, 0, 0, 1);
+ regs.u.r32.ebx = 1;
+ regs.u.r32.eax = 0x534D4150;
+ regs.u.r32.ecx = 0x14;
+ CLEAR_CF();
+ return;
+ break;
+ case 1:
+ set_e820_range(ES, regs.u.r16.di,
+ 0x0009f000L, 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:
+#if BX_ROMBIOS32
+ set_e820_range(ES, regs.u.r16.di,
+ 0x00100000L,
+ extended_memory_size - ACPI_DATA_SIZE ,0, 0, 1);
+ regs.u.r32.ebx = 4;
+#else
+ set_e820_range(ES, regs.u.r16.di,
+ 0x00100000L,
+ extended_memory_size, 1);
+ regs.u.r32.ebx = 5;
+#endif
+ 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:
+ /* 4 pages before the bios, 3 pages for vmx tss pages,
+ * the other page for EPT real mode pagetable */
+ set_e820_range(ES, regs.u.r16.di, 0xfffbc000L,
+ 0xfffc0000L, 0, 0, 2);
+ regs.u.r32.ebx = 6;
+ 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_low, lba_high;
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit16u cylinder, head, sector;
+ Bit16u segment, offset;
+ Bit16u npc, nph, npspt, nlc, nlh, nlspt;
+ Bit16u size, count;
+ Bit8u device, status;
+
+ BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+ write_byte(0x0040, 0x008e, 0); // clear completion flag
+
+ // basic check : device has to be defined
+ if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
+ BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
+ goto int13_fail;
+ }
+
+ // Get the ata channel
+ device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
+
+ // basic check : device has to be valid
+ if (device >= BX_MAX_ATA_DEVICES) {
+ BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
+ goto int13_fail;
+ }
+
+ switch (GET_AH()) {
+
+ case 0x00: /* disk controller reset */
+ ata_reset (device);
+ goto int13_success;
+ break;
+
+ case 0x01: /* read disk status */
+ status = read_byte(0x0040, 0x0074);
+ SET_AH(status);
+ SET_DISK_RET_STATUS(0);
+ /* set CF if error status read */
+ if (status) goto int13_fail_nostatus;
+ else goto int13_success_noah;
+ break;
+
+ case 0x02: // read disk sectors
+ case 0x03: // write disk sectors
+ case 0x04: // verify disk sectors
+
+ count = GET_AL();
+ cylinder = GET_CH();
+ cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
+ sector = (GET_CL() & 0x3f);
+ head = GET_DH();
+
+ segment = ES;
+ offset = BX;
+
+ if ((count > 128) || (count == 0) || (sector == 0)) {
+ BX_INFO("int13_harddisk: function %02x, parameter out of range!\n",GET_AH());
+ goto int13_fail;
+ }
+
+ nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
+ nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
+ nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
+
+ // sanity check on cyl heads, sec
+ if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
+ BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
+ goto int13_fail;
+ }
+
+ // FIXME verify
+ if ( GET_AH() == 0x04 ) goto int13_success;
+
+ nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
+ npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
+
+ // if needed, translate lchs to lba, and execute command
+ if ( (nph != nlh) || (npspt != nlspt)) {
+ lba_low = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
+ lba_high = 0;
+ sector = 0; // this forces the command to be lba
+ }
+
+ if ( GET_AH() == 0x02 )
+ status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
+ else
+ status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba_low, lba_high, segment, offset);
+
+ // Set nb of sector transferred
+ SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
+
+ if (status != 0) {
+ BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
+ SET_AH(0x0c);
+ goto int13_fail_noah;
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x05: /* format disk track */
+ BX_INFO("format disk track called\n");
+ goto int13_success;
+ return;
+ break;
+
+ case 0x08: /* read disk drive parameters */
+
+ // Get logical geometry from table
+ nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
+ nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
+ nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
+ count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
+
+ nlc = nlc - 2; /* 0 based , last sector not used */
+ SET_AL(0);
+ SET_CH(nlc & 0xff);
+ SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
+ SET_DH(nlh - 1);
+ SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
+
+ // FIXME should set ES & DI
+
+ goto int13_success;
+ break;
+
+ case 0x10: /* check drive ready */
+ // should look at 40:8E also???
+
+ // Read the status from controller
+ status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
+ if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
+ goto int13_success;
+ }
+ else {
+ SET_AH(0xAA);
+ goto int13_fail_noah;
+ }
+ break;
+
+ case 0x15: /* read disk drive size */
+
+ // Get logical geometry from table
+ nlc = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
+ nlh = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
+ nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
+
+ // Compute sector count seen by int13
+ lba_low = (Bit32u)(nlc - 1) * (Bit32u)nlh * (Bit32u)nlspt;
+ CX = lba_low >> 16;
+ DX = lba_low & 0xffff;
+
+ SET_AH(3); // hard disk accessible
+ goto int13_success_noah;
+ break;
+
+ case 0x41: // IBM/MS installation check
+ BX=0xaa55; // install check
+ SET_AH(0x30); // EDD 3.0
+ CX=0x0007; // ext disk access and edd, removable supported
+ goto int13_success_noah;
+ break;
+
+ case 0x42: // IBM/MS extended read
+ case 0x43: // IBM/MS extended write
+ case 0x44: // IBM/MS verify
+ case 0x47: // IBM/MS extended seek
+
+ count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
+ segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
+ offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
+
+ // Get 32 msb lba and check
+ lba_high=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
+ if (lba_high > read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high) ) {
+ BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
+ goto int13_fail;
+ }
+
+ // Get 32 lsb lba and check
+ lba_low=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
+ if (lba_high == read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high)
+ && lba_low >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low) ) {
+ BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
+ goto int13_fail;
+ }
+
+ // If verify or seek
+ if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
+ goto int13_success;
+
+ // Execute the command
+ if ( GET_AH() == 0x42 )
+ status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
+ else
+ status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba_low, lba_high, segment, offset);
+
+ count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
+ write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
+
+ if (status != 0) {
+ BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
+ SET_AH(0x0c);
+ goto int13_fail_noah;
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x45: // IBM/MS lock/unlock drive
+ case 0x49: // IBM/MS extended media change
+ goto int13_success; // Always success for HD
+ break;
+
+ case 0x46: // IBM/MS eject media
+ SET_AH(0xb2); // Volume Not Removable
+ goto int13_fail_noah; // Always fail for HD
+ break;
+
+ case 0x48: // IBM/MS get drive parameters
+ size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
+
+ // Buffer is too small
+ if(size < 0x1a)
+ goto int13_fail;
+
+ // EDD 1.x
+ if(size >= 0x1a) {
+ Bit16u blksize;
+
+ npc = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
+ nph = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
+ npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
+ lba_low = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_low);
+ lba_high = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors_high);
+ blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
+ if (lba_high || (lba_low/npspt)/nph > 0x3fff)
+ {
+ write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x00); // geometry is invalid
+ write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0x3fff);
+ }
+ else
+ {
+ write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
+ write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
+ }
+ write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba_low);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, lba_high);
+ write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
+ }
+
+ // EDD 2.x
+ if(size >= 0x1e) {
+ Bit8u channel, dev, irq, mode, checksum, i, translation;
+ Bit16u iobase1, iobase2, options;
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
+ write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
+
+ // Fill in dpte
+ channel = device / 2;
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+ iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+ irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
+ mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+ translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
+
+ options = (translation==ATA_TRANSLATION_NONE?0:1)<<3; // chs translation
+ options |= (1<<4); // lba translation
+ options |= (mode==ATA_MODE_PIO32?1:0)<<7;
+ options |= (translation==ATA_TRANSLATION_LBA?1:0)<<9;
+ options |= (translation==ATA_TRANSLATION_RECHS?3:0)<<9;
+
+ write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
+ write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
+ write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
+ write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
+ write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
+ if (size >=0x42)
+ write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
+ else
+ write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x10);
+
+ checksum=0;
+ for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
+ checksum = ~checksum;
+ write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
+ }
+
+ // EDD 3.x
+ if(size >= 0x42) {
+ Bit8u channel, iface, checksum, i;
+ Bit16u iobase1;
+
+ channel = device / 2;
+ iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
+ write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
+ write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
+ write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
+ write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
+
+ if (iface==ATA_IFACE_ISA) {
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
+ }
+ else {
+ // FIXME PCI
+ }
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
+
+ if (iface==ATA_IFACE_ISA) {
+ write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
+ write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
+ }
+ else {
+ // FIXME PCI
+ }
+ write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
+ write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
+ write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
+
+ checksum=0;
+ for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
+ checksum = ~checksum;
+ write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x4e: // // IBM/MS set hardware configuration
+ // DMA, prefetch, PIO maximum not supported
+ switch (GET_AL()) {
+ case 0x01:
+ case 0x03:
+ case 0x04:
+ case 0x06:
+ goto int13_success;
+ break;
+ default :
+ goto int13_fail;
+ }
+ break;
+
+ case 0x09: /* initialize drive parameters */
+ case 0x0c: /* seek to specified cylinder */
+ case 0x0d: /* alternate disk reset */
+ case 0x11: /* recalibrate */
+ case 0x14: /* controller internal diagnostic */
+ BX_INFO("int13_harddisk: function %02xh unimplemented, returns success\n", GET_AH());
+ goto int13_success;
+ break;
+
+ case 0x0a: /* read disk sectors with ECC */
+ case 0x0b: /* write disk sectors with ECC */
+ case 0x18: // set media type for format
+ case 0x50: // IBM/MS send packet command
+ default:
+ BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH());
+ goto int13_fail;
+ break;
+ }
+
+int13_fail:
+ SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+int13_fail_noah:
+ SET_DISK_RET_STATUS(GET_AH());
+int13_fail_nostatus:
+ SET_CF(); // error occurred
+ return;
+
+int13_success:
+ SET_AH(0x00); // no error
+int13_success_noah:
+ SET_DISK_RET_STATUS(0x00);
+ CLEAR_CF(); // no error
+ return;
+}
+
+// ---------------------------------------------------------------------------
+// Start of int13 for cdrom
+// ---------------------------------------------------------------------------
+
+ void
+int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+ Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit8u device, status, locks;
+ Bit8u atacmd[12];
+ Bit32u lba;
+ Bit16u count, segment, offset, i, size;
+
+ BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+ SET_DISK_RET_STATUS(0x00);
+
+ /* basic check : device should be 0xE0+ */
+ if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
+ BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
+ goto int13_fail;
+ }
+
+ // Get the ata channel
+ device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
+
+ /* basic check : device has to be valid */
+ if (device >= BX_MAX_ATA_DEVICES) {
+ BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
+ goto int13_fail;
+ }
+
+ switch (GET_AH()) {
+
+ // all those functions return SUCCESS
+ case 0x00: /* disk controller reset */
+ case 0x09: /* initialize drive parameters */
+ case 0x0c: /* seek to specified cylinder */
+ case 0x0d: /* alternate disk reset */
+ case 0x10: /* check drive ready */
+ case 0x11: /* recalibrate */
+ case 0x14: /* controller internal diagnostic */
+ case 0x16: /* detect disk change */
+ goto int13_success;
+ break;
+
+ // all those functions return disk write-protected
+ case 0x03: /* write disk sectors */
+ case 0x05: /* format disk track */
+ case 0x43: // IBM/MS extended write
+ SET_AH(0x03);
+ goto int13_fail_noah;
+ break;
+
+ case 0x01: /* read disk status */
+ status = read_byte(0x0040, 0x0074);
+ SET_AH(status);
+ SET_DISK_RET_STATUS(0);
+
+ /* set CF if error status read */
+ if (status) goto int13_fail_nostatus;
+ else goto int13_success_noah;
+ break;
+
+ case 0x15: /* read disk drive size */
+ SET_AH(0x02);
+ goto int13_fail_noah;
+ break;
+
+ case 0x41: // IBM/MS installation check
+ BX=0xaa55; // install check
+ SET_AH(0x30); // EDD 2.1
+ CX=0x0007; // ext disk access, removable and edd
+ goto int13_success_noah;
+ break;
+
+ case 0x42: // IBM/MS extended read
+ case 0x44: // IBM/MS verify sectors
+ case 0x47: // IBM/MS extended seek
+
+ count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
+ segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
+ offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
+
+ // Can't use 64 bits lba
+ lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
+ if (lba != 0L) {
+ BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
+ goto int13_fail;
+ }
+
+ // Get 32 bits lba
+ lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
+
+ // If verify or seek
+ if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
+ goto int13_success;
+
+ memsetb(get_SS(),atacmd,0,12);
+ atacmd[0]=0x28; // READ command
+ atacmd[7]=(count & 0xff00) >> 8; // Sectors
+ atacmd[8]=(count & 0x00ff); // Sectors
+ atacmd[2]=(lba & 0xff000000) >> 24; // LBA
+ atacmd[3]=(lba & 0x00ff0000) >> 16;
+ atacmd[4]=(lba & 0x0000ff00) >> 8;
+ atacmd[5]=(lba & 0x000000ff);
+ status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset);
+
+ count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
+ write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
+
+ if (status != 0) {
+ BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
+ SET_AH(0x0c);
+ goto int13_fail_noah;
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x45: // IBM/MS lock/unlock drive
+ if (GET_AL() > 2) goto int13_fail;
+
+ locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
+
+ switch (GET_AL()) {
+ case 0 : // lock
+ if (locks == 0xff) {
+ SET_AH(0xb4);
+ SET_AL(1);
+ goto int13_fail_noah;
+ }
+ write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
+ SET_AL(1);
+ break;
+ case 1 : // unlock
+ if (locks == 0x00) {
+ SET_AH(0xb0);
+ SET_AL(0);
+ goto int13_fail_noah;
+ }
+ write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
+ SET_AL(locks==0?0:1);
+ break;
+ case 2 : // status
+ SET_AL(locks==0?0:1);
+ break;
+ }
+ goto int13_success;
+ break;
+
+ case 0x46: // IBM/MS eject media
+ locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
+
+ if (locks != 0) {
+ SET_AH(0xb1); // media locked
+ goto int13_fail_noah;
+ }
+ // FIXME should handle 0x31 no media in device
+ // FIXME should handle 0xb5 valid request failed
+
+ // Call removable media eject
+ ASM_START
+ push bp
+ mov bp, sp
+
+ mov ah, #0x52
+ int #0x15
+ mov _int13_cdrom.status + 2[bp], ah
+ jnc int13_cdrom_rme_end
+ mov _int13_cdrom.status, #1
+int13_cdrom_rme_end:
+ pop bp
+ ASM_END
+
+ if (status != 0) {
+ SET_AH(0xb1); // media locked
+ goto int13_fail_noah;
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x48: // IBM/MS get drive parameters
+ size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
+
+ // Buffer is too small
+ if(size < 0x1a)
+ goto int13_fail;
+
+ // EDD 1.x
+ if(size >= 0x1a) {
+ Bit16u cylinders, heads, spt, blksize;
+
+ blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
+ write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
+ write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff); // FIXME should be Bit64
+ write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);
+ write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);
+ }
+
+ // EDD 2.x
+ if(size >= 0x1e) {
+ Bit8u channel, dev, irq, mode, checksum, i;
+ Bit16u iobase1, iobase2, options;
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);
+ write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);
+
+ // Fill in dpte
+ channel = device / 2;
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+ iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+ irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
+ mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+
+ // FIXME atapi device
+ options = (1<<4); // lba translation
+ options |= (1<<5); // removable device
+ options |= (1<<6); // atapi device
+ options |= (mode==ATA_MODE_PIO32?1:0<<7);
+
+ write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
+ write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2 + ATA_CB_DC);
+ write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
+ write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
+ write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
+ write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
+ write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
+
+ checksum=0;
+ for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, ((Bit8u*)(&EbdaData->ata.dpte)) + i);
+ checksum = ~checksum;
+ write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
+ }
+
+ // EDD 3.x
+ if(size >= 0x42) {
+ Bit8u channel, iface, checksum, i;
+ Bit16u iobase1;
+
+ channel = device / 2;
+ iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
+ iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+
+ write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
+ write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
+ write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
+ write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
+ write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
+
+ if (iface==ATA_IFACE_ISA) {
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
+ }
+ else {
+ // FIXME PCI
+ }
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
+ write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
+
+ if (iface==ATA_IFACE_ISA) {
+ write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
+ write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
+ }
+ else {
+ // FIXME PCI
+ }
+ write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
+ write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
+ write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
+ write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
+
+ checksum=0;
+ for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
+ checksum = ~checksum;
+ write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x49: // IBM/MS extended media change
+ // always send changed ??
+ SET_AH(06);
+ goto int13_fail_nostatus;
+ break;
+
+ case 0x4e: // // IBM/MS set hardware configuration
+ // DMA, prefetch, PIO maximum not supported
+ switch (GET_AL()) {
+ case 0x01:
+ case 0x03:
+ case 0x04:
+ case 0x06:
+ goto int13_success;
+ break;
+ default :
+ goto int13_fail;
+ }
+ break;
+
+ // all those functions return unimplemented
+ case 0x02: /* read sectors */
+ case 0x04: /* verify sectors */
+ case 0x08: /* read disk drive parameters */
+ case 0x0a: /* read disk sectors with ECC */
+ case 0x0b: /* write disk sectors with ECC */
+ case 0x18: /* set media type for format */
+ case 0x50: // ? - send packet command
+ default:
+ BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
+ goto int13_fail;
+ break;
+ }
+
+int13_fail:
+ SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+int13_fail_noah:
+ SET_DISK_RET_STATUS(GET_AH());
+int13_fail_nostatus:
+ SET_CF(); // error occurred
+ return;
+
+int13_success:
+ SET_AH(0x00); // no error
+int13_success_noah:
+ SET_DISK_RET_STATUS(0x00);
+ CLEAR_CF(); // no error
+ return;
+}
+
+// ---------------------------------------------------------------------------
+// End of int13 for cdrom
+// ---------------------------------------------------------------------------
+
+#if BX_ELTORITO_BOOT
+// ---------------------------------------------------------------------------
+// Start of int13 for eltorito functions
+// ---------------------------------------------------------------------------
+
+ void
+int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
+ Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+ BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+ // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
+
+ switch (GET_AH()) {
+
+ // FIXME ElTorito Various. Should be implemented
+ case 0x4a: // ElTorito - Initiate disk emu
+ case 0x4c: // ElTorito - Initiate disk emu and boot
+ case 0x4d: // ElTorito - Return Boot catalog
+ BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
+ goto int13_fail;
+ break;
+
+ case 0x4b: // ElTorito - Terminate disk emu
+ // FIXME ElTorito Hardcoded
+ write_byte(DS,SI+0x00,0x13);
+ write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
+ write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
+ write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
+ write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
+ write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
+ write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
+ write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
+ write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
+ write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
+ write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
+ write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
+
+ // If we have to terminate emulation
+ if(GET_AL() == 0x00) {
+ // FIXME ElTorito Various. Should be handled accordingly to spec
+ write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
+ }
+
+ goto int13_success;
+ break;
+
+ default:
+ BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
+ goto int13_fail;
+ break;
+ }
+
+int13_fail:
+ SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+ SET_DISK_RET_STATUS(GET_AH());
+ SET_CF(); // error occurred
+ return;
+
+int13_success:
+ SET_AH(0x00); // no error
+ SET_DISK_RET_STATUS(0x00);
+ CLEAR_CF(); // no error
+ return;
+}
+
+// ---------------------------------------------------------------------------
+// End of int13 for eltorito functions
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Start of int13 when emulating a device from the cd
+// ---------------------------------------------------------------------------
+
+ void
+int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
+ Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit8u device, status;
+ Bit16u vheads, vspt, vcylinders;
+ Bit16u head, sector, cylinder, nbsectors;
+ Bit32u vlba, ilba, slba, elba;
+ Bit16u before, segment, offset;
+ Bit8u atacmd[12];
+
+ BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+ /* at this point, we are emulating a floppy/harddisk */
+
+ // Recompute the device number
+ device = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
+ device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
+
+ SET_DISK_RET_STATUS(0x00);
+
+ /* basic checks : emulation should be active, dl should equal the emulated drive */
+ if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
+ || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
+ BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
+ goto int13_fail;
+ }
+
+ switch (GET_AH()) {
+
+ // all those functions return SUCCESS
+ case 0x00: /* disk controller reset */
+ case 0x09: /* initialize drive parameters */
+ case 0x0c: /* seek to specified cylinder */
+ case 0x0d: /* alternate disk reset */ // FIXME ElTorito Various. should really reset ?
+ case 0x10: /* check drive ready */ // FIXME ElTorito Various. should check if ready ?
+ case 0x11: /* recalibrate */
+ case 0x14: /* controller internal diagnostic */
+ case 0x16: /* detect disk change */
+ goto int13_success;
+ break;
+
+ // all those functions return disk write-protected
+ case 0x03: /* write disk sectors */
+ case 0x05: /* format disk track */
+ SET_AH(0x03);
+ goto int13_fail_noah;
+ break;
+
+ case 0x01: /* read disk status */
+ status=read_byte(0x0040, 0x0074);
+ SET_AH(status);
+ SET_DISK_RET_STATUS(0);
+
+ /* set CF if error status read */
+ if (status) goto int13_fail_nostatus;
+ else goto int13_success_noah;
+ break;
+
+ case 0x02: // read disk sectors
+ case 0x04: // verify disk sectors
+ vspt = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
+ vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders);
+ vheads = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads);
+
+ ilba = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
+
+ sector = GET_CL() & 0x003f;
+ cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
+ head = GET_DH();
+ nbsectors = GET_AL();
+ segment = ES;
+ offset = BX;
+
+ // no sector to read ?
+ if(nbsectors==0) goto int13_success;
+
+ // sanity checks sco openserver needs this!
+ if ((sector > vspt)
+ || (cylinder >= vcylinders)
+ || (head >= vheads)) {
+ goto int13_fail;
+ }
+
+ // After controls, verify do nothing
+ if (GET_AH() == 0x04) goto int13_success;
+
+ segment = ES+(BX / 16);
+ offset = BX % 16;
+
+ // calculate the virtual lba inside the image
+ vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
+
+ // In advance so we don't loose the count
+ SET_AL(nbsectors);
+
+ // start lba on cd
+ slba = (Bit32u)vlba/4;
+ before= (Bit16u)vlba%4;
+
+ // end lba on cd
+ elba = (Bit32u)(vlba+nbsectors-1)/4;
+
+ memsetb(get_SS(),atacmd,0,12);
+ atacmd[0]=0x28; // READ command
+ atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
+ atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff); // Sectors
+ atacmd[2]=(ilba+slba & 0xff000000) >> 24; // LBA
+ atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
+ atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
+ atacmd[5]=(ilba+slba & 0x000000ff);
+ if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
+ BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
+ SET_AH(0x02);
+ SET_AL(0);
+ goto int13_fail_noah;
+ }
+
+ goto int13_success;
+ break;
+
+ case 0x08: /* read disk drive parameters */
+ vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt);
+ vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1;
+ vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1;
+
+ SET_AL( 0x00 );
+ SET_BL( 0x00 );
+ SET_CH( vcylinders & 0xff );
+ SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt & 0x3f ));
+ SET_DH( vheads );
+ SET_DL( 0x02 ); // FIXME ElTorito Various. should send the real count of drives 1 or 2
+ // FIXME ElTorito Harddisk. should send the HD count
+
+ switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
+ case 0x01: SET_BL( 0x02 ); break;
+ case 0x02: SET_BL( 0x04 ); break;
+ case 0x03: SET_BL( 0x06 ); break;
+ }
+
+ASM_START
+ push bp
+ mov bp, sp
+ mov ax, #diskette_param_table2
+ mov _int13_cdemu.DI+2[bp], ax
+ mov _int13_cdemu.ES+2[bp], cs
+ pop bp
+ASM_END
+ goto int13_success;
+ break;
+
+ case 0x15: /* read disk drive size */
+ // FIXME ElTorito Harddisk. What geometry to send ?
+ SET_AH(0x03);
+ goto int13_success_noah;
+ break;
+
+ // all those functions return unimplemented
+ case 0x0a: /* read disk sectors with ECC */
+ case 0x0b: /* write disk sectors with ECC */
+ case 0x18: /* set media type for format */
+ case 0x41: // IBM/MS installation check
+ // FIXME ElTorito Harddisk. Darwin would like to use EDD
+ case 0x42: // IBM/MS extended read
+ case 0x43: // IBM/MS extended write
+ case 0x44: // IBM/MS verify sectors
+ case 0x45: // IBM/MS lock/unlock drive
+ case 0x46: // IBM/MS eject media
+ case 0x47: // IBM/MS extended seek
+ case 0x48: // IBM/MS get drive parameters
+ case 0x49: // IBM/MS extended media change
+ case 0x4e: // ? - set hardware configuration
+ case 0x50: // ? - send packet command
+ default:
+ BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
+ goto int13_fail;
+ break;
+ }
+
+int13_fail:
+ SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+int13_fail_noah:
+ SET_DISK_RET_STATUS(GET_AH());
+int13_fail_nostatus:
+ SET_CF(); // error occurred
+ return;
+
+int13_success:
+ SET_AH(0x00); // no error
+int13_success_noah:
+ SET_DISK_RET_STATUS(0x00);
+ CLEAR_CF(); // no error
+ return;
+}
+
+// ---------------------------------------------------------------------------
+// End of int13 when emulating a device from the cd
+// ---------------------------------------------------------------------------
+
+#endif // BX_ELTORITO_BOOT
+
+#else //BX_USE_ATADRV
+
+ void
+outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
+ Bit16u cylinder;
+ Bit16u hd_heads;
+ Bit16u head;
+ Bit16u hd_sectors;
+ Bit16u sector;
+ Bit16u dl;
+{
+ASM_START
+ push bp
+ mov bp, sp
+ push eax
+ push ebx
+ push edx
+ xor eax,eax
+ mov ax,4[bp] // cylinder
+ xor ebx,ebx
+ mov bl,6[bp] // hd_heads
+ imul ebx
+
+ mov bl,8[bp] // head
+ add eax,ebx
+ mov bl,10[bp] // hd_sectors
+ imul ebx
+ mov bl,12[bp] // sector
+ add eax,ebx
+
+ dec eax
+ mov dx,#0x1f3
+ out dx,al
+ mov dx,#0x1f4
+ mov al,ah
+ out dx,al
+ shr eax,#16
+ mov dx,#0x1f5
+ out dx,al
+ and ah,#0xf
+ mov bl,14[bp] // dl
+ and bl,#1
+ shl bl,#4
+ or ah,bl
+ or ah,#0xe0
+ mov al,ah
+ mov dx,#0x01f6
+ out dx,al
+ pop edx
+ pop ebx
+ pop eax
+ pop bp
+ASM_END
+}
+
+ void
+int13_harddisk(EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+ Bit16u EHAX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+ Bit8u drive, num_sectors, sector, head, status, mod;
+ Bit8u drive_map;
+ Bit8u n_drives;
+ Bit16u cyl_mod, ax;
+ Bit16u max_cylinder, cylinder, total_sectors;
+ Bit16u hd_cylinders;
+ Bit8u hd_heads, hd_sectors;
+ Bit16u val16;
+ Bit8u sector_count;
+ unsigned int i;
+ Bit16u tempbx;
+ Bit16u dpsize;
+
+ Bit16u count, segment, offset;
+ Bit32u lba;
+ Bit16u error;
+
+ BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+ write_byte(0x0040, 0x008e, 0); // clear completion flag
+
+ /* at this point, DL is >= 0x80 to be passed from the floppy int13h
+ handler code */
+ /* check how many disks first (cmos reg 0x12), return an error if
+ drive not present */
+ drive_map = inb_cmos(0x12);
+ drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
+ (((drive_map & 0x0f)==0) ? 0 : 2);
+ n_drives = (drive_map==0) ? 0 :
+ ((drive_map==3) ? 2 : 1);
+
+ if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
+ SET_AH(0x01);
+ SET_DISK_RET_STATUS(0x01);
+ SET_CF(); /* error occurred */
+ return;
+ }
+
+ switch (GET_AH()) {
+
+ case 0x00: /* disk controller reset */
+BX_DEBUG_INT13_HD("int13_f00\n");
+
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ set_diskette_ret_status(0);
+ set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
+ set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x01: /* read disk status */
+BX_DEBUG_INT13_HD("int13_f01\n");
+ status = read_byte(0x0040, 0x0074);
+ SET_AH(status);
+ SET_DISK_RET_STATUS(0);
+ /* set CF if error status read */
+ if (status) SET_CF();
+ else CLEAR_CF();
+ return;
+ break;
+
+ case 0x04: // verify disk sectors
+ case 0x02: // read disk sectors
+ drive = GET_ELDL();
+ get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+
+ num_sectors = GET_AL();
+ cylinder = (GET_CL() & 0x00c0) << 2 | GET_CH();
+ sector = (GET_CL() & 0x3f);
+ head = GET_DH();
+
+
+ if (hd_cylinders > 1024) {
+ if (hd_cylinders <= 2048) {
+ cylinder <<= 1;
+ }
+ else if (hd_cylinders <= 4096) {
+ cylinder <<= 2;
+ }
+ else if (hd_cylinders <= 8192) {
+ cylinder <<= 3;
+ }
+ else { // hd_cylinders <= 16384
+ cylinder <<= 4;
+ }
+
+ ax = head / hd_heads;
+ cyl_mod = ax & 0xff;
+ head = ax >> 8;
+ cylinder |= cyl_mod;
+ }
+
+ if ( (cylinder >= hd_cylinders) ||
+ (sector > hd_sectors) ||
+ (head >= hd_heads) ) {
+ SET_AH(1);
+ SET_DISK_RET_STATUS(1);
+ SET_CF(); /* error occurred */
+ return;
+ }
+
+ if ( (num_sectors > 128) || (num_sectors == 0) )
+ BX_PANIC("int13_harddisk: num_sectors out of range!\n");
+
+ if (head > 15)
+ BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
+
+ if ( GET_AH() == 0x04 ) {
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF();
+ return;
+ }
+
+ status = inb(0x1f7);
+ if (status & 0x80) {
+ BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
+ }
+ outb(0x01f2, num_sectors);
+ /* activate LBA? (tomv) */
+ if (hd_heads > 16) {
+BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
+ outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
+ }
+ else {
+ outb(0x01f3, sector);
+ outb(0x01f4, cylinder & 0x00ff);
+ outb(0x01f5, cylinder >> 8);
+ outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
+ }
+ outb(0x01f7, 0x20);
+
+ while (1) {
+ status = inb(0x1f7);
+ if ( !(status & 0x80) ) break;
+ }
+
+ if (status & 0x01) {
+ BX_PANIC("hard drive BIOS:(read/verify) read error\n");
+ } else if ( !(status & 0x08) ) {
+ BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
+ BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
+ }
+
+ sector_count = 0;
+ tempbx = BX;
+
+ASM_START
+ sti ;; enable higher priority interrupts
+ASM_END
+
+ while (1) {
+ASM_START
+ ;; store temp bx in real DI register
+ push bp
+ mov bp, sp
+ mov di, _int13_harddisk.tempbx + 2 [bp]
+ pop bp
+
+ ;; adjust if there will be an overrun
+ cmp di, #0xfe00
+ jbe i13_f02_no_adjust
+i13_f02_adjust:
+ sub di, #0x0200 ; sub 512 bytes from offset
+ mov ax, es
+ add ax, #0x0020 ; add 512 to segment
+ mov es, ax
+
+i13_f02_no_adjust:
+ mov cx, #0x0100 ;; counter (256 words = 512b)
+ mov dx, #0x01f0 ;; AT data read port
+
+ rep
+ insw ;; CX words transfered from port(DX) to ES:[DI]
+
+i13_f02_done:
+ ;; store real DI register back to temp bx
+ push bp
+ mov bp, sp
+ mov _int13_harddisk.tempbx + 2 [bp], di
+ pop bp
+ASM_END
+
+ sector_count++;
+ num_sectors--;
+ if (num_sectors == 0) {
+ status = inb(0x1f7);
+ if ( (status & 0xc9) != 0x40 )
+ BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
+ break;
+ }
+ else {
+ status = inb(0x1f7);
+ if ( (status & 0xc9) != 0x48 )
+ BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
+ continue;
+ }
+ }
+
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ SET_AL(sector_count);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+
+ case 0x03: /* write disk sectors */
+BX_DEBUG_INT13_HD("int13_f03\n");
+ drive = GET_ELDL ();
+ get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+
+ num_sectors = GET_AL();
+ cylinder = GET_CH();
+ cylinder |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
+ sector = (GET_CL() & 0x3f);
+ head = GET_DH();
+
+ if (hd_cylinders > 1024) {
+ if (hd_cylinders <= 2048) {
+ cylinder <<= 1;
+ }
+ else if (hd_cylinders <= 4096) {
+ cylinder <<= 2;
+ }
+ else if (hd_cylinders <= 8192) {
+ cylinder <<= 3;
+ }
+ else { // hd_cylinders <= 16384
+ cylinder <<= 4;
+ }
+
+ ax = head / hd_heads;
+ cyl_mod = ax & 0xff;
+ head = ax >> 8;
+ cylinder |= cyl_mod;
+ }
+
+ if ( (cylinder >= hd_cylinders) ||
+ (sector > hd_sectors) ||
+ (head >= hd_heads) ) {
+ SET_AH( 1);
+ SET_DISK_RET_STATUS(1);
+ SET_CF(); /* error occurred */
+ return;
+ }
+
+ if ( (num_sectors > 128) || (num_sectors == 0) )
+ BX_PANIC("int13_harddisk: num_sectors out of range!\n");
+
+ if (head > 15)
+ BX_PANIC("hard drive BIOS:(read) head > 15\n");
+
+ status = inb(0x1f7);
+ if (status & 0x80) {
+ BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
+ }
+// should check for Drive Ready Bit also in status reg
+ outb(0x01f2, num_sectors);
+
+ /* activate LBA? (tomv) */
+ if (hd_heads > 16) {
+BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
+ outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
+ }
+ else {
+ outb(0x01f3, sector);
+ outb(0x01f4, cylinder & 0x00ff);
+ outb(0x01f5, cylinder >> 8);
+ outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
+ }
+ outb(0x01f7, 0x30);
+
+ // wait for busy bit to turn off after seeking
+ while (1) {
+ status = inb(0x1f7);
+ if ( !(status & 0x80) ) break;
+ }
+
+ if ( !(status & 0x08) ) {
+ BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
+ BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
+ }
+
+ sector_count = 0;
+ tempbx = BX;
+
+ASM_START
+ sti ;; enable higher priority interrupts
+ASM_END
+
+ while (1) {
+ASM_START
+ ;; store temp bx in real SI register
+ push bp
+ mov bp, sp
+ mov si, _int13_harddisk.tempbx + 2 [bp]
+ pop bp
+
+ ;; adjust if there will be an overrun
+ cmp si, #0xfe00
+ jbe i13_f03_no_adjust
+i13_f03_adjust:
+ sub si, #0x0200 ; sub 512 bytes from offset
+ mov ax, es
+ add ax, #0x0020 ; add 512 to segment
+ mov es, ax
+
+i13_f03_no_adjust:
+ mov cx, #0x0100 ;; counter (256 words = 512b)
+ mov dx, #0x01f0 ;; AT data read port
+
+ seg ES
+ rep
+ outsw ;; CX words tranfered from ES:[SI] to port(DX)
+
+ ;; store real SI register back to temp bx
+ push bp
+ mov bp, sp
+ mov _int13_harddisk.tempbx + 2 [bp], si
+ pop bp
+ASM_END
+
+ sector_count++;
+ num_sectors--;
+ if (num_sectors == 0) {
+ status = inb(0x1f7);
+ if ( (status & 0xe9) != 0x40 )
+ BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
+ break;
+ }
+ else {
+ status = inb(0x1f7);
+ if ( (status & 0xc9) != 0x48 )
+ BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
+ continue;
+ }
+ }
+
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ SET_AL(sector_count);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x05: /* format disk track */
+BX_DEBUG_INT13_HD("int13_f05\n");
+ BX_PANIC("format disk track called\n");
+ /* nop */
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x08: /* read disk drive parameters */
+BX_DEBUG_INT13_HD("int13_f08\n");
+
+ drive = GET_ELDL ();
+ get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+
+ // translate CHS
+ //
+ if (hd_cylinders <= 1024) {
+ // hd_cylinders >>= 0;
+ // hd_heads <<= 0;
+ }
+ else if (hd_cylinders <= 2048) {
+ hd_cylinders >>= 1;
+ hd_heads <<= 1;
+ }
+ else if (hd_cylinders <= 4096) {
+ hd_cylinders >>= 2;
+ hd_heads <<= 2;
+ }
+ else if (hd_cylinders <= 8192) {
+ hd_cylinders >>= 3;
+ hd_heads <<= 3;
+ }
+ else { // hd_cylinders <= 16384
+ hd_cylinders >>= 4;
+ hd_heads <<= 4;
+ }
+
+ max_cylinder = hd_cylinders - 2; /* 0 based */
+ SET_AL(0);
+ SET_CH(max_cylinder & 0xff);
+ SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
+ SET_DH(hd_heads - 1);
+ SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+
+ return;
+ break;
+
+ case 0x09: /* initialize drive parameters */
+BX_DEBUG_INT13_HD("int13_f09\n");
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x0a: /* read disk sectors with ECC */
+BX_DEBUG_INT13_HD("int13_f0a\n");
+ case 0x0b: /* write disk sectors with ECC */
+BX_DEBUG_INT13_HD("int13_f0b\n");
+ BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
+ return;
+ break;
+
+ case 0x0c: /* seek to specified cylinder */
+BX_DEBUG_INT13_HD("int13_f0c\n");
+ BX_INFO("int13h function 0ch (seek) not implemented!\n");
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x0d: /* alternate disk reset */
+BX_DEBUG_INT13_HD("int13_f0d\n");
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x10: /* check drive ready */
+BX_DEBUG_INT13_HD("int13_f10\n");
+ //SET_AH(0);
+ //SET_DISK_RET_STATUS(0);
+ //CLEAR_CF(); /* successful */
+ //return;
+ //break;
+
+ // should look at 40:8E also???
+ status = inb(0x01f7);
+ if ( (status & 0xc0) == 0x40 ) {
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); // drive ready
+ return;
+ }
+ else {
+ SET_AH(0xAA);
+ SET_DISK_RET_STATUS(0xAA);
+ SET_CF(); // not ready
+ return;
+ }
+ break;
+
+ case 0x11: /* recalibrate */
+BX_DEBUG_INT13_HD("int13_f11\n");
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+ return;
+ break;
+
+ case 0x14: /* controller internal diagnostic */
+BX_DEBUG_INT13_HD("int13_f14\n");
+ SET_AH(0);
+ SET_DISK_RET_STATUS(0);
+ CLEAR_CF(); /* successful */
+ SET_AL(0);
+ return;
+ break;
+
+ case 0x15: /* read disk drive size */
+ drive = GET_ELDL();
+ get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+ASM_START
+ push bp
+ mov bp, sp
+ mov al, _int13_harddisk.hd_heads + 2 [bp]
+ mov ah, _int13_harddisk.hd_sectors + 2 [bp]
+ mul al, ah ;; ax = heads * sectors
+ mov bx, _int13_harddisk.hd_cylinders + 2 [bp]
+ dec bx ;; use (cylinders - 1) ???
+ mul ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
+ ;; now we need to move the 32bit result dx:ax to what the
+ ;; BIOS wants which is cx:dx.
+ ;; and then into CX:DX on the stack
+ mov _int13_harddisk.CX + 2 [bp], dx
+ mov _int13_harddisk.DX + 2 [bp], ax
+ pop bp
+ASM_END
+ SET_AH(3); // hard disk accessible
+ SET_DISK_RET_STATUS(0); // ??? should this be 0
+ CLEAR_CF(); // successful
+ return;
+ break;
+
+ case 0x18: // set media type for format
+ case 0x41: // IBM/MS
+ case 0x42: // IBM/MS
+ case 0x43: // IBM/MS
+ case 0x44: // IBM/MS
+ case 0x45: // IBM/MS lock/unlock drive
+ case 0x46: // IBM/MS eject media
+ case 0x47: // IBM/MS extended seek
+ case 0x49: // IBM/MS extended media change
+ case 0x50: // IBM/MS send packet command
+ default:
+ BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
+
+ SET_AH(1); // code=invalid function in AH or invalid parameter
+ SET_DISK_RET_STATUS(1);
+ SET_CF(); /* unsuccessful */
+ return;
+ break;
+ }
+}
+
+static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
+static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
+
+ void
+get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
+ Bit8u drive;
+ Bit16u *hd_cylinders;
+ Bit8u *hd_heads;
+ Bit8u *hd_sectors;
+{
+ Bit8u hd_type;
+ Bit16u ss;
+ Bit16u cylinders;
+ Bit8u iobase;
+
+ ss = get_SS();
+ if (drive == 0x80) {
+ hd_type = inb_cmos(0x12) & 0xf0;
+ if (hd_type != 0xf0)
+ BX_INFO(panic_msg_reg12h,0);
+ hd_type = inb_cmos(0x19); // HD0: extended type
+ if (hd_type != 47)
+ BX_INFO(panic_msg_reg19h,0,0x19);
+ iobase = 0x1b;
+ } else {
+ hd_type = inb_cmos(0x12) & 0x0f;
+ if (hd_type != 0x0f)
+ BX_INFO(panic_msg_reg12h,1);
+ hd_type = inb_cmos(0x1a); // HD1: extended type
+ if (hd_type != 47)
+ BX_INFO(panic_msg_reg19h,0,0x1a);
+ iobase = 0x24;
+ }
+
+ // cylinders
+ cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
+ write_word(ss, hd_cylinders, cylinders);
+
+ // heads
+ write_byte(ss, hd_heads, inb_cmos(iobase+2));
+
+ // sectors per track
+ write_byte(ss, hd_sectors, inb_cmos(iobase+8));
+}
+
+#endif //else BX_USE_ATADRV
+
+#if BX_SUPPORT_FLOPPY
+
+//////////////////////
+// FLOPPY functions //
+//////////////////////
+
+void floppy_reset_controller()
+{
+ Bit8u val8;
+
+ // Reset controller
+ val8 = inb(0x03f2);
+ outb(0x03f2, val8 & ~0x04);
+ outb(0x03f2, val8 | 0x04);
+
+ // Wait for controller to come out of reset
+ do {
+ val8 = inb(0x3f4);
+ } while ( (val8 & 0xc0) != 0x80 );
+}
+
+void floppy_prepare_controller(drive)
+ Bit16u drive;
+{
+ Bit8u val8, dor, prev_reset;
+
+ // set 40:3e bit 7 to 0
+ val8 = read_byte(0x0040, 0x003e);
+ val8 &= 0x7f;
+ write_byte(0x0040, 0x003e, val8);
+
+ // turn on motor of selected drive, DMA & int enabled, normal operation
+ prev_reset = inb(0x03f2) & 0x04;
+ if (drive)
+ dor = 0x20;
+ else
+ dor = 0x10;
+ dor |= 0x0c;
+ dor |= drive;
+ outb(0x03f2, dor);
+
+ // reset the disk motor timeout value of INT 08
+ write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
+
+ // wait for drive readiness
+ do {
+ val8 = inb(0x3f4);
+ } while ( (val8 & 0xc0) != 0x80 );
+
+ if (prev_reset == 0) {
+ // turn on interrupts
+ASM_START
+ sti
+ASM_END
+ // wait on 40:3e bit 7 to become 1
+ do {
+ val8 = read_byte(0x0040, 0x003e);
+ } while ( (val8 & 0x80) == 0 );
+ val8 &= 0x7f;
+ASM_START
+ cli
+ASM_END
+ write_byte(0x0040, 0x003e, val8);
+ }
+}
+
+ bx_bool
+floppy_media_known(drive)
+ Bit16u drive;
+{
+ Bit8u val8;
+ Bit16u media_state_offset;
+
+ val8 = read_byte(0x0040, 0x003e); // diskette recal status
+ if (drive)
+ val8 >>= 1;
+ val8 &= 0x01;
+ if (val8 == 0)
+ return(0);
+
+ media_state_offset = 0x0090;
+ if (drive)
+ media_state_offset += 1;
+
+ val8 = read_byte(0x0040, media_state_offset);
+ val8 = (val8 >> 4) & 0x01;
+ if (val8 == 0)
+ return(0);
+
+ // check pass, return KNOWN
+ return(1);
+}
+
+ bx_bool
+floppy_media_sense(drive)
+ Bit16u drive;
+{
+ bx_bool retval;
+ Bit16u media_state_offset;
+ Bit8u drive_type, config_data, media_state;
+
+ if (floppy_drive_recal(drive) == 0) {
+ return(0);
+ }
+
+ // for now cheat and get drive type from CMOS,
+ // assume media is same as drive type
+
+ // ** config_data **
+ // Bitfields for diskette media control:
+ // Bit(s) Description (Table M0028)
+ // 7-6 last data rate set by controller
+ // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+ // 5-4 last diskette drive step rate selected
+ // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
+ // 3-2 {data rate at start of operation}
+ // 1-0 reserved
+
+ // ** media_state **
+ // Bitfields for diskette drive media state:
+ // Bit(s) Description (Table M0030)
+ // 7-6 data rate
+ // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+ // 5 double stepping required (e.g. 360kB in 1.2MB)
+ // 4 media type established
+ // 3 drive capable of supporting 4MB media
+ // 2-0 on exit from BIOS, contains
+ // 000 trying 360kB in 360kB
+ // 001 trying 360kB in 1.2MB
+ // 010 trying 1.2MB in 1.2MB
+ // 011 360kB in 360kB established
+ // 100 360kB in 1.2MB established
+ // 101 1.2MB in 1.2MB established
+ // 110 reserved
+ // 111 all other formats/drives
+
+ drive_type = inb_cmos(0x10);
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+ if ( drive_type == 1 ) {
+ // 360K 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x25; // 0010 0101
+ retval = 1;
+ }
+ else if ( drive_type == 2 ) {
+ // 1.2 MB 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
+ retval = 1;
+ }
+ else if ( drive_type == 3 ) {
+ // 720K 3.5" drive
+ config_data = 0x00; // 0000 0000 ???
+ media_state = 0x17; // 0001 0111
+ retval = 1;
+ }
+ else if ( drive_type == 4 ) {
+ // 1.44 MB 3.5" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x17; // 0001 0111
+ retval = 1;
+ }
+ else if ( drive_type == 5 ) {
+ // 2.88 MB 3.5" drive
+ config_data = 0xCC; // 1100 1100
+ media_state = 0xD7; // 1101 0111
+ retval = 1;
+ }
+ //
+ // Extended floppy size uses special cmos setting
+ else if ( drive_type == 6 ) {
+ // 160k 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x27; // 0010 0111
+ retval = 1;
+ }
+ else if ( drive_type == 7 ) {
+ // 180k 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x27; // 0010 0111
+ retval = 1;
+ }
+ else if ( drive_type == 8 ) {
+ // 320k 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x27; // 0010 0111
+ retval = 1;
+ }
+
+ else {
+ // not recognized
+ config_data = 0x00; // 0000 0000
+ media_state = 0x00; // 0000 0000
+ retval = 0;
+ }
+
+ if (drive == 0)
+ media_state_offset = 0x90;
+ else
+ media_state_offset = 0x91;
+ write_byte(0x0040, 0x008B, config_data);
+ write_byte(0x0040, media_state_offset, media_state);
+
+ return(retval);
+}
+
+ bx_bool
+floppy_drive_recal(drive)
+ Bit16u drive;
+{
+ Bit8u val8;
+ Bit16u curr_cyl_offset;
+
+ floppy_prepare_controller(drive);
+
+ // send Recalibrate command (2 bytes) to controller
+ outb(0x03f5, 0x07); // 07: Recalibrate
+ outb(0x03f5, drive); // 0=drive0, 1=drive1
+
+ // turn on interrupts
+ASM_START
+ sti
+ASM_END
+
+ // wait on 40:3e bit 7 to become 1
+ do {
+ val8 = (read_byte(0x0040, 0x003e) & 0x80);
+ } while ( val8 == 0 );
+
+ val8 = 0; // separate asm from while() loop
+ // turn off interrupts
+ASM_START
+ cli
+ASM_END
+
+ // set 40:3e bit 7 to 0, and calibrated bit
+ val8 = read_byte(0x0040, 0x003e);
+ val8 &= 0x7f;
+ if (drive) {
+ val8 |= 0x02; // Drive 1 calibrated
+ curr_cyl_offset = 0x0095;
+ } else {
+ val8 |= 0x01; // Drive 0 calibrated
+ curr_cyl_offset = 0x0094;
+ }
+ write_byte(0x0040, 0x003e, val8);
+ write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
+
+ return(1);
+}
+
+
+
+ bx_bool
+floppy_drive_exists(drive)
+ Bit16u drive;
+{
+ Bit8u drive_type;
+
+ // check CMOS to see if drive exists
+ drive_type = inb_cmos(0x10);
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+ if ( drive_type == 0 )
+ return(0);
+ else
+ return(1);
+}
+
+ void
+int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+ Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+ Bit8u drive, num_sectors, track, sector, head, status;
+ Bit16u base_address, base_count, base_es;
+ Bit8u page, mode_register, val8, dor;
+ Bit8u return_status[7];
+ Bit8u drive_type, num_floppies, ah;
+ Bit16u es, last_addr;
+
+ BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+ ah = GET_AH();
+
+ switch ( ah ) {
+ case 0x00: // diskette controller reset
+BX_DEBUG_INT13_FL("floppy f00\n");
+ drive = GET_ELDL();
+ if (drive > 1) {
+ SET_AH(1); // invalid param
+ set_diskette_ret_status(1);
+ SET_CF();
+ return;
+ }
+ drive_type = inb_cmos(0x10);
+
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+ if (drive_type == 0) {
+ SET_AH(0x80); // drive not responding
+ set_diskette_ret_status(0x80);
+ SET_CF();
+ return;
+ }
+ SET_AH(0);
+ set_diskette_ret_status(0);
+ CLEAR_CF(); // successful
+ set_diskette_current_cyl(drive, 0); // current cylinder
+ return;
+
+ case 0x01: // Read Diskette Status
+ CLEAR_CF();
+ val8 = read_byte(0x0000, 0x0441);
+ SET_AH(val8);
+ if (val8) {
+ SET_CF();
+ }
+ return;
+
+ case 0x02: // Read Diskette Sectors
+ case 0x03: // Write Diskette Sectors
+ case 0x04: // Verify Diskette Sectors
+ num_sectors = GET_AL();
+ track = GET_CH();
+ sector = GET_CL();
+ head = GET_DH();
+ drive = GET_ELDL();
+
+ if ((drive > 1) || (head > 1) || (sector == 0) ||
+ (num_sectors == 0) || (num_sectors > 72)) {
+ BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+ SET_AH(1);
+ set_diskette_ret_status(1);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+
+ // see if drive exists
+ if (floppy_drive_exists(drive) == 0) {
+ SET_AH(0x80); // not responding
+ set_diskette_ret_status(0x80);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+
+ // see if media in drive, and type is known
+ if (floppy_media_known(drive) == 0) {
+ if (floppy_media_sense(drive) == 0) {
+ SET_AH(0x0C); // Media type not found
+ set_diskette_ret_status(0x0C);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+ }
+
+ if (ah == 0x02) {
+ // Read Diskette Sectors
+
+ //-----------------------------------
+ // set up DMA controller for transfer
+ //-----------------------------------
+
+ // es:bx = pointer to where to place information from diskette
+ // port 04: DMA-1 base and current address, channel 2
+ // port 05: DMA-1 base and current count, channel 2
+ page = (ES >> 12); // upper 4 bits
+ base_es = (ES << 4); // lower 16bits contributed by ES
+ base_address = base_es + BX; // lower 16 bits of address
+ // contributed by ES:BX
+ if ( base_address < base_es ) {
+ // in case of carry, adjust page by 1
+ page++;
+ }
+ base_count = (num_sectors * 512) - 1;
+
+ // check for 64K boundary overrun
+ last_addr = base_address + base_count;
+ if (last_addr < base_address) {
+ SET_AH(0x09);
+ set_diskette_ret_status(0x09);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+
+ BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
+ outb(0x000a, 0x06);
+
+ BX_DEBUG_INT13_FL("clear flip-flop\n");
+ outb(0x000c, 0x00); // clear flip-flop
+ outb(0x0004, base_address);
+ outb(0x0004, base_address>>8);
+ BX_DEBUG_INT13_FL("clear flip-flop\n");
+ outb(0x000c, 0x00); // clear flip-flop
+ outb(0x0005, base_count);
+ outb(0x0005, base_count>>8);
+
+ // port 0b: DMA-1 Mode Register
+ mode_register = 0x46; // single mode, increment, autoinit disable,
+ // transfer type=write, channel 2
+ BX_DEBUG_INT13_FL("setting mode register\n");
+ outb(0x000b, mode_register);
+
+ BX_DEBUG_INT13_FL("setting page register\n");
+ // port 81: DMA-1 Page Register, channel 2
+ outb(0x0081, page);
+
+ BX_DEBUG_INT13_FL("unmask chan 2\n");
+ outb(0x000a, 0x02); // unmask channel 2
+
+ BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
+ outb(0x000a, 0x02);
+
+ //--------------------------------------
+ // set up floppy controller for transfer
+ //--------------------------------------
+ floppy_prepare_controller(drive);
+
+ // send read-normal-data command (9 bytes) to controller
+ outb(0x03f5, 0xe6); // e6: read normal data
+ outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
+ outb(0x03f5, track);
+ outb(0x03f5, head);
+ outb(0x03f5, sector);
+ outb(0x03f5, 2); // 512 byte sector size
+ outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
+ outb(0x03f5, 0); // Gap length
+ outb(0x03f5, 0xff); // Gap length
+
+ // turn on interrupts
+ ASM_START
+ sti
+ ASM_END
+
+ // wait on 40:3e bit 7 to become 1
+ do {
+ val8 = read_byte(0x0040, 0x0040);
+ if (val8 == 0) {
+ floppy_reset_controller();
+ SET_AH(0x80); // drive not ready (timeout)
+ set_diskette_ret_status(0x80);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+ val8 = (read_byte(0x0040, 0x003e) & 0x80);
+ } while ( val8 == 0 );
+
+ val8 = 0; // separate asm from while() loop
+ // turn off interrupts
+ ASM_START
+ cli
+ ASM_END
+
+ // set 40:3e bit 7 to 0
+ val8 = read_byte(0x0040, 0x003e);
+ val8 &= 0x7f;
+ write_byte(0x0040, 0x003e, val8);
+
+ // check port 3f4 for accessibility to status bytes
+ val8 = inb(0x3f4);
+ if ( (val8 & 0xc0) != 0xc0 )
+ BX_PANIC("int13_diskette: ctrl not ready\n");
+
+ // read 7 return status bytes from controller
+ // using loop index broken, have to unroll...
+ return_status[0] = inb(0x3f5);
+ return_status[1] = inb(0x3f5);
+ return_status[2] = inb(0x3f5);
+ return_status[3] = inb(0x3f5);
+ return_status[4] = inb(0x3f5);
+ return_status[5] = inb(0x3f5);
+ return_status[6] = inb(0x3f5);
+ // record in BIOS Data Area
+ write_byte(0x0040, 0x0042, return_status[0]);
+ write_byte(0x0040, 0x0043, return_status[1]);
+ write_byte(0x0040, 0x0044, return_status[2]);
+ write_byte(0x0040, 0x0045, return_status[3]);
+ write_byte(0x0040, 0x0046, return_status[4]);
+ write_byte(0x0040, 0x0047, return_status[5]);
+ write_byte(0x0040, 0x0048, return_status[6]);
+
+ if ( (return_status[0] & 0xc0) != 0 ) {
+ SET_AH(0x20);
+ set_diskette_ret_status(0x20);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+
+ // ??? should track be new val from return_status[3] ?
+ set_diskette_current_cyl(drive, track);
+ // AL = number of sectors read (same value as passed)
+ SET_AH(0x00); // success
+ CLEAR_CF(); // success
+ return;
+ } else if (ah == 0x03) {
+ // Write Diskette Sectors
+
+ //-----------------------------------
+ // set up DMA controller for transfer
+ //-----------------------------------
+
+ // es:bx = pointer to where to place information from diskette
+ // port 04: DMA-1 base and current address, channel 2
+ // port 05: DMA-1 base and current count, channel 2
+ page = (ES >> 12); // upper 4 bits
+ base_es = (ES << 4); // lower 16bits contributed by ES
+ base_address = base_es + BX; // lower 16 bits of address
+ // contributed by ES:BX
+ if ( base_address < base_es ) {
+ // in case of carry, adjust page by 1
+ page++;
+ }
+ base_count = (num_sectors * 512) - 1;
+
+ // check for 64K boundary overrun
+ last_addr = base_address + base_count;
+ if (last_addr < base_address) {
+ SET_AH(0x09);
+ set_diskette_ret_status(0x09);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+
+ BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
+ outb(0x000a, 0x06);
+
+ outb(0x000c, 0x00); // clear flip-flop
+ outb(0x0004, base_address);
+ outb(0x0004, base_address>>8);
+ outb(0x000c, 0x00); // clear flip-flop
+ outb(0x0005, base_count);
+ outb(0x0005, base_count>>8);
+
+ // port 0b: DMA-1 Mode Register
+ mode_register = 0x4a; // single mode, increment, autoinit disable,
+ // transfer type=read, channel 2
+ outb(0x000b, mode_register);
+
+ // port 81: DMA-1 Page Register, channel 2
+ outb(0x0081, page);
+
+ BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
+ outb(0x000a, 0x02);
+
+ //--------------------------------------
+ // set up floppy controller for transfer
+ //--------------------------------------
+ floppy_prepare_controller(drive);
+
+ // send write-normal-data command (9 bytes) to controller
+ outb(0x03f5, 0xc5); // c5: write normal data
+ outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
+ outb(0x03f5, track);
+ outb(0x03f5, head);
+ outb(0x03f5, sector);
+ outb(0x03f5, 2); // 512 byte sector size
+ outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
+ outb(0x03f5, 0); // Gap length
+ outb(0x03f5, 0xff); // Gap length
+
+ // turn on interrupts
+ ASM_START
+ sti
+ ASM_END
+
+ // wait on 40:3e bit 7 to become 1
+ do {
+ val8 = read_byte(0x0040, 0x0040);
+ if (val8 == 0) {
+ floppy_reset_controller();
+ SET_AH(0x80); // drive not ready (timeout)
+ set_diskette_ret_status(0x80);
+ SET_AL(0); // no sectors written
+ SET_CF(); // error occurred
+ return;
+ }
+ val8 = (read_byte(0x0040, 0x003e) & 0x80);
+ } while ( val8 == 0 );
+
+ val8 = 0; // separate asm from while() loop
+ // turn off interrupts
+ ASM_START
+ cli
+ ASM_END
+
+ // set 40:3e bit 7 to 0
+ val8 = read_byte(0x0040, 0x003e);
+ val8 &= 0x7f;
+ write_byte(0x0040, 0x003e, val8);
+
+ // check port 3f4 for accessibility to status bytes
+ val8 = inb(0x3f4);
+ if ( (val8 & 0xc0) != 0xc0 )
+ BX_PANIC("int13_diskette: ctrl not ready\n");
+
+ // read 7 return status bytes from controller
+ // using loop index broken, have to unroll...
+ return_status[0] = inb(0x3f5);
+ return_status[1] = inb(0x3f5);
+ return_status[2] = inb(0x3f5);
+ return_status[3] = inb(0x3f5);
+ return_status[4] = inb(0x3f5);
+ return_status[5] = inb(0x3f5);
+ return_status[6] = inb(0x3f5);
+ // record in BIOS Data Area
+ write_byte(0x0040, 0x0042, return_status[0]);
+ write_byte(0x0040, 0x0043, return_status[1]);
+ write_byte(0x0040, 0x0044, return_status[2]);
+ write_byte(0x0040, 0x0045, return_status[3]);
+ write_byte(0x0040, 0x0046, return_status[4]);
+ write_byte(0x0040, 0x0047, return_status[5]);
+ write_byte(0x0040, 0x0048, return_status[6]);
+
+ if ( (return_status[0] & 0xc0) != 0 ) {
+ if ( (return_status[1] & 0x02) != 0 ) {
+ // diskette not writable.
+ // AH=status code=0x03 (tried to write on write-protected disk)
+ // AL=number of sectors written=0
+ AX = 0x0300;
+ SET_CF();
+ return;
+ } else {
+ BX_PANIC("int13_diskette_function: read error\n");
+ }
+ }
+
+ // ??? should track be new val from return_status[3] ?
+ set_diskette_current_cyl(drive, track);
+ // AL = number of sectors read (same value as passed)
+ SET_AH(0x00); // success
+ CLEAR_CF(); // success
+ return;
+ } else { // if (ah == 0x04)
+ // Verify Diskette Sectors
+
+ // ??? should track be new val from return_status[3] ?
+ set_diskette_current_cyl(drive, track);
+ // AL = number of sectors verified (same value as passed)
+ CLEAR_CF(); // success
+ SET_AH(0x00); // success
+ return;
+ }
+ break;
+
+ case 0x05: // format diskette track
+BX_DEBUG_INT13_FL("floppy f05\n");
+
+ num_sectors = GET_AL();
+ track = GET_CH();
+ head = GET_DH();
+ drive = GET_ELDL();
+
+ if ((drive > 1) || (head > 1) || (track > 79) ||
+ (num_sectors == 0) || (num_sectors > 18)) {
+ SET_AH(1);
+ set_diskette_ret_status(1);
+ SET_CF(); // error occurred
+ }
+
+ // see if drive exists
+ if (floppy_drive_exists(drive) == 0) {
+ SET_AH(0x80); // drive not responding
+ set_diskette_ret_status(0x80);
+ SET_CF(); // error occurred
+ return;
+ }
+
+ // see if media in drive, and type is known
+ if (floppy_media_known(drive) == 0) {
+ if (floppy_media_sense(drive) == 0) {
+ SET_AH(0x0C); // Media type not found
+ set_diskette_ret_status(0x0C);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+ }
+
+ // set up DMA controller for transfer
+ page = (ES >> 12); // upper 4 bits
+ base_es = (ES << 4); // lower 16bits contributed by ES
+ base_address = base_es + BX; // lower 16 bits of address
+ // contributed by ES:BX
+ if ( base_address < base_es ) {
+ // in case of carry, adjust page by 1
+ page++;
+ }
+ base_count = (num_sectors * 4) - 1;
+
+ // check for 64K boundary overrun
+ last_addr = base_address + base_count;
+ if (last_addr < base_address) {
+ SET_AH(0x09);
+ set_diskette_ret_status(0x09);
+ SET_AL(0); // no sectors read
+ SET_CF(); // error occurred
+ return;
+ }
+
+ outb(0x000a, 0x06);
+ outb(0x000c, 0x00); // clear flip-flop
+ outb(0x0004, base_address);
+ outb(0x0004, base_address>>8);
+ outb(0x000c, 0x00); // clear flip-flop
+ outb(0x0005, base_count);
+ outb(0x0005, base_count>>8);
+ mode_register = 0x4a; // single mode, increment, autoinit disable,
+ // transfer type=read, channel 2
+ outb(0x000b, mode_register);
+ // port 81: DMA-1 Page Register, channel 2
+ outb(0x0081, page);
+ outb(0x000a, 0x02);
+
+ // set up floppy controller for transfer
+ floppy_prepare_controller(drive);
+
+ // send format-track command (6 bytes) to controller
+ outb(0x03f5, 0x4d); // 4d: format track
+ outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
+ outb(0x03f5, 2); // 512 byte sector size
+ outb(0x03f5, num_sectors); // number of sectors per track
+ outb(0x03f5, 0); // Gap length
+ outb(0x03f5, 0xf6); // Fill byte
+ // turn on interrupts
+ ASM_START
+ sti
+ ASM_END
+
+ // wait on 40:3e bit 7 to become 1
+ do {
+ val8 = read_byte(0x0040, 0x0040);
+ if (val8 == 0) {
+ floppy_reset_controller();
+ SET_AH(0x80); // drive not ready (timeout)
+ set_diskette_ret_status(0x80);
+ SET_CF(); // error occurred
+ return;
+ }
+ val8 = (read_byte(0x0040, 0x003e) & 0x80);
+ } while ( val8 == 0 );
+
+ val8 = 0; // separate asm from while() loop
+ // turn off interrupts
+ ASM_START
+ cli
+ ASM_END
+ // set 40:3e bit 7 to 0
+ val8 = read_byte(0x0040, 0x003e);
+ val8 &= 0x7f;
+ write_byte(0x0040, 0x003e, val8);
+ // check port 3f4 for accessibility to status bytes
+ val8 = inb(0x3f4);
+ if ( (val8 & 0xc0) != 0xc0 )
+ BX_PANIC("int13_diskette: ctrl not ready\n");
+
+ // read 7 return status bytes from controller
+ // using loop index broken, have to unroll...
+ return_status[0] = inb(0x3f5);
+ return_status[1] = inb(0x3f5);
+ return_status[2] = inb(0x3f5);
+ return_status[3] = inb(0x3f5);
+ return_status[4] = inb(0x3f5);
+ return_status[5] = inb(0x3f5);
+ return_status[6] = inb(0x3f5);
+ // record in BIOS Data Area
+ write_byte(0x0040, 0x0042, return_status[0]);
+ write_byte(0x0040, 0x0043, return_status[1]);
+ write_byte(0x0040, 0x0044, return_status[2]);
+ write_byte(0x0040, 0x0045, return_status[3]);
+ write_byte(0x0040, 0x0046, return_status[4]);
+ write_byte(0x0040, 0x0047, return_status[5]);
+ write_byte(0x0040, 0x0048, return_status[6]);
+
+ if ( (return_status[0] & 0xc0) != 0 ) {
+ if ( (return_status[1] & 0x02) != 0 ) {
+ // diskette not writable.
+ // AH=status code=0x03 (tried to write on write-protected disk)
+ // AL=number of sectors written=0
+ AX = 0x0300;
+ SET_CF();
+ return;
+ } else {
+ BX_PANIC("int13_diskette_function: write error\n");
+ }
+ }
+
+ SET_AH(0);
+ set_diskette_ret_status(0);
+ set_diskette_current_cyl(drive, 0);
+ CLEAR_CF(); // successful
+ return;
+
+
+ case 0x08: // read diskette drive parameters
+BX_DEBUG_INT13_FL("floppy f08\n");
+ drive = GET_ELDL();
+
+ if (drive > 1) {
+ AX = 0;
+ BX = 0;
+ CX = 0;
+ DX = 0;
+ ES = 0;
+ DI = 0;
+ SET_DL(num_floppies);
+ SET_CF();
+ return;
+ }
+
+ drive_type = inb_cmos(0x10);
+ num_floppies = 0;
+ if (drive_type & 0xf0)
+ num_floppies++;
+ if (drive_type & 0x0f)
+ num_floppies++;
+
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+
+ SET_BH(0);
+ SET_BL(drive_type);
+ SET_AH(0);
+ SET_AL(0);
+ SET_DL(num_floppies);
+
+ switch (drive_type) {
+ case 0: // none
+ CX = 0;
+ SET_DH(0); // max head #
+ break;
+
+ case 1: // 360KB, 5.25"
+ CX = 0x2709; // 40 tracks, 9 sectors
+ SET_DH(1); // max head #
+ break;
+
+ case 2: // 1.2MB, 5.25"
+ CX = 0x4f0f; // 80 tracks, 15 sectors
+ SET_DH(1); // max head #
+ break;
+
+ case 3: // 720KB, 3.5"
+ CX = 0x4f09; // 80 tracks, 9 sectors
+ SET_DH(1); // max head #
+ break;
+
+ case 4: // 1.44MB, 3.5"
+ CX = 0x4f12; // 80 tracks, 18 sectors
+ SET_DH(1); // max head #
+ break;
+
+ case 5: // 2.88MB, 3.5"
+ CX = 0x4f24; // 80 tracks, 36 sectors
+ SET_DH(1); // max head #
+ break;
+
+ case 6: // 160k, 5.25"
+ CX = 0x2708; // 40 tracks, 8 sectors
+ SET_DH(0); // max head #
+ break;
+
+ case 7: // 180k, 5.25"
+ CX = 0x2709; // 40 tracks, 9 sectors
+ SET_DH(0); // max head #
+ break;
+
+ case 8: // 320k, 5.25"
+ CX = 0x2708; // 40 tracks, 8 sectors
+ SET_DH(1); // max head #
+ break;
+
+ default: // ?
+ BX_PANIC("floppy: int13: bad floppy type\n");
+ }
+
+ /* set es & di to point to 11 byte diskette param table in ROM */
+ASM_START
+ push bp
+ mov bp, sp
+ mov ax, #diskette_param_table2
+ mov _int13_diskette_function.DI+2[bp], ax
+ mov _int13_diskette_function.ES+2[bp], cs
+ pop bp
+ASM_END
+ CLEAR_CF(); // success
+ /* disk status not changed upon success */
+ return;
+
+
+ case 0x15: // read diskette drive type
+BX_DEBUG_INT13_FL("floppy f15\n");
+ drive = GET_ELDL();
+ if (drive > 1) {
+ SET_AH(0); // only 2 drives supported
+ // set_diskette_ret_status here ???
+ SET_CF();
+ return;
+ }
+ drive_type = inb_cmos(0x10);
+
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+ CLEAR_CF(); // successful, not present
+ if (drive_type==0) {
+ SET_AH(0); // drive not present
+ }
+ else {
+ SET_AH(1); // drive present, does not support change line
+ }
+
+ return;
+
+ case 0x16: // get diskette change line status
+BX_DEBUG_INT13_FL("floppy f16\n");
+ drive = GET_ELDL();
+ if (drive > 1) {
+ SET_AH(0x01); // invalid drive
+ set_diskette_ret_status(0x01);
+ SET_CF();
+ return;
+ }
+
+ SET_AH(0x06); // change line not supported
+ set_diskette_ret_status(0x06);
+ SET_CF();
+ return;
+
+ case 0x17: // set diskette type for format(old)
+BX_DEBUG_INT13_FL("floppy f17\n");
+ /* not used for 1.44M floppies */
+ SET_AH(0x01); // not supported
+ set_diskette_ret_status(1); /* not supported */
+ SET_CF();
+ return;
+
+ case 0x18: // set diskette type for format(new)
+BX_DEBUG_INT13_FL("floppy f18\n");
+ SET_AH(0x01); // do later
+ set_diskette_ret_status(1);
+ SET_CF();
+ return;
+
+ default:
+ BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
+
+ // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
+ SET_AH(0x01); // ???
+ set_diskette_ret_status(1);
+ SET_CF();
+ return;
+ // }
+ }
+}
+#else // #if BX_SUPPORT_FLOPPY
+ void
+int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+ Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+ Bit8u val8;
+
+ switch ( GET_AH() ) {
+
+ case 0x01: // Read Diskette Status
+ CLEAR_CF();
+ val8 = read_byte(0x0000, 0x0441);
+ SET_AH(val8);
+ if (val8) {
+ SET_CF();
+ }
+ return;
+
+ default:
+ SET_CF();
+ write_byte(0x0000, 0x0441, 0x01);
+ SET_AH(0x01);
+ }
+}
+#endif // #if BX_SUPPORT_FLOPPY
+
+ void
+set_diskette_ret_status(value)
+ Bit8u value;
+{
+ write_byte(0x0040, 0x0041, value);
+}
+
+ void
+set_diskette_current_cyl(drive, cyl)
+ Bit8u drive;
+ Bit8u cyl;
+{
+ if (drive > 1)
+ BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
+ write_byte(0x0040, 0x0094+drive, cyl);
+}
+
+ void
+determine_floppy_media(drive)
+ Bit16u drive;
+{
+#if 0
+ Bit8u val8, DOR, ctrl_info;
+
+ ctrl_info = read_byte(0x0040, 0x008F);
+ if (drive==1)
+ ctrl_info >>= 4;
+ else
+ ctrl_info &= 0x0f;
+
+#if 0
+ if (drive == 0) {
+ DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
+ }
+ else {
+ DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
+ }
+#endif
+
+ if ( (ctrl_info & 0x04) != 0x04 ) {
+ // Drive not determined means no drive exists, done.
+ return;
+ }
+
+#if 0
+ // check Main Status Register for readiness
+ val8 = inb(0x03f4) & 0x80; // Main Status Register
+ if (val8 != 0x80)
+ BX_PANIC("d_f_m: MRQ bit not set\n");
+
+ // change line
+
+ // existing BDA values
+
+ // turn on drive motor
+ outb(0x03f2, DOR); // Digital Output Register
+ //
+#endif
+ BX_PANIC("d_f_m: OK so far\n");
+#endif
+}
+
+ void
+int17_function(regs, ds, iret_addr)
+ pusha_regs_t regs; // regs pushed from PUSHA instruction
+ Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+ iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
+{
+ Bit16u addr,timeout;
+ Bit8u val8;
+
+ ASM_START
+ sti
+ ASM_END
+
+ addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
+ if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
+ timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
+ if (regs.u.r8.ah == 0) {
+ outb(addr, regs.u.r8.al);
+ val8 = inb(addr+2);
+ outb(addr+2, val8 | 0x01); // send strobe
+ ASM_START
+ nop
+ ASM_END
+ outb(addr+2, val8 & ~0x01);
+ while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
+ timeout--;
+ }
+ }
+ if (regs.u.r8.ah == 1) {
+ val8 = inb(addr+2);
+ outb(addr+2, val8 & ~0x04); // send init
+ ASM_START
+ nop
+ ASM_END
+ outb(addr+2, val8 | 0x04);
+ }
+ val8 = inb(addr+1);
+ regs.u.r8.ah = (val8 ^ 0x48);
+ if (!timeout) regs.u.r8.ah |= 0x01;
+ ClearCF(iret_addr.flags);
+ } else {
+ SetCF(iret_addr.flags); // Unsupported
+ }
+}
+
+void
+int19_function(seq_nr)
+Bit16u seq_nr;
+{
+ Bit16u ebda_seg=read_word(0x0040,0x000E);
+ Bit16u bootdev;
+ Bit8u bootdrv;
+ Bit8u bootchk;
+ Bit16u bootseg;
+ Bit16u bootip;
+ Bit16u status;
+ Bit16u bootfirst;
+
+ ipl_entry_t e;
+
+ // if BX_ELTORITO_BOOT is not defined, old behavior
+ // check bit 5 in CMOS reg 0x2d. load either 0x00 or 0x80 into DL
+ // in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
+ // 0: system boot sequence, first drive C: then A:
+ // 1: system boot sequence, first drive A: then C:
+ // else BX_ELTORITO_BOOT is defined
+ // CMOS regs 0x3D and 0x38 contain the boot sequence:
+ // CMOS reg 0x3D & 0x0f : 1st boot device
+ // CMOS reg 0x3D & 0xf0 : 2nd boot device
+ // CMOS reg 0x38 & 0xf0 : 3rd boot device
+ // boot device codes:
+ // 0x00 : not defined
+ // 0x01 : first floppy
+ // 0x02 : first harddrive
+ // 0x03 : first cdrom
+ // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot)
+ // else : boot failure
+
+ // Get the boot sequence
+#if BX_ELTORITO_BOOT
+ bootdev = inb_cmos(0x3d);
+ bootdev |= ((inb_cmos(0x38) & 0xf0) << 4);
+ bootdev >>= 4 * seq_nr;
+ bootdev &= 0xf;
+
+ /* Read user selected device */
+ bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET);
+ if (bootfirst != 0xFFFF) {
+ bootdev = bootfirst;
+ /* User selected device not set */
+ write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
+ /* Reset boot sequence */
+ write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF);
+ } else if (bootdev == 0) BX_PANIC("No bootable device.\n");
+
+ /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
+ bootdev -= 1;
+#else
+ if (seq_nr ==2) BX_PANIC("No more boot devices.");
+ if (!!(inb_cmos(0x2d) & 0x20) ^ (seq_nr == 1))
+ /* Boot from floppy if the bit is set or it's the second boot */
+ bootdev = 0x00;
+ else
+ bootdev = 0x01;
+#endif
+
+ /* Read the boot device from the IPL table */
+ if (get_boot_vector(bootdev, &e) == 0) {
+ BX_INFO("Invalid boot device (0x%x)\n", bootdev);
+ return;
+ }
+
+ /* Do the loading, and set up vector as a far pointer to the boot
+ * address, and bootdrv as the boot drive */
+ print_boot_device(&e);
+
+ switch(e.type) {
+ case IPL_TYPE_FLOPPY: /* FDD */
+ case IPL_TYPE_HARDDISK: /* HDD */
+
+ bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
+ bootseg = 0x07c0;
+ status = 0;
+
+ASM_START
+ push bp
+ mov bp, sp
+ push ax
+ push bx
+ push cx
+ push dx
+
+ mov dl, _int19_function.bootdrv + 2[bp]
+ mov ax, _int19_function.bootseg + 2[bp]
+ mov es, ax ;; segment
+ xor bx, bx ;; offset
+ mov ah, #0x02 ;; function 2, read diskette sector
+ mov al, #0x01 ;; read 1 sector
+ mov ch, #0x00 ;; track 0
+ mov cl, #0x01 ;; sector 1
+ mov dh, #0x00 ;; head 0
+ int #0x13 ;; read sector
+ jnc int19_load_done
+ mov ax, #0x0001
+ mov _int19_function.status + 2[bp], ax
+
+int19_load_done:
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ pop bp
+ASM_END
+
+ if (status != 0) {
+ print_boot_failure(e.type, 1);
+ return;
+ }
+
+ /* Always check the signature on a HDD boot sector; on FDD, only do
+ * the check if the CMOS doesn't tell us to skip it */
+ if ((e.type != IPL_TYPE_FLOPPY) || !((inb_cmos(0x38) & 0x01))) {
+ if (read_word(bootseg,0x1fe) != 0xaa55) {
+ print_boot_failure(e.type, 0);
+ return;
+ }
+ }
+
+ /* Canonicalize bootseg:bootip */
+ bootip = (bootseg & 0x0fff) << 4;
+ bootseg &= 0xf000;
+ break;
+
+#if BX_ELTORITO_BOOT
+ case IPL_TYPE_CDROM: /* CD-ROM */
+ status = cdrom_boot();
+
+ // If failure
+ if ( (status & 0x00ff) !=0 ) {
+ print_cdromboot_failure(status);
+ print_boot_failure(e.type, 1);
+ return;
+ }
+
+ bootdrv = (Bit8u)(status>>8);
+ bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
+ bootip = 0;
+ break;
+#endif
+
+ case IPL_TYPE_BEV: /* Expansion ROM with a Bootstrap Entry Vector (a far pointer) */
+ bootseg = e.vector >> 16;
+ bootip = e.vector & 0xffff;
+ break;
+
+ default: return;
+ }
+
+ /* Debugging info */
+ BX_INFO("Booting from %x:%x\n", bootseg, bootip);
+
+ /* Jump to the boot vector */
+ASM_START
+ mov bp, sp
+ push cs
+ push #int18_handler
+ ;; Build an iret stack frame that will take us to the boot vector.
+ ;; iret pops ip, then cs, then flags, so push them in the opposite order.
+ pushf
+ mov ax, _int19_function.bootseg + 0[bp]
+ push ax
+ mov ax, _int19_function.bootip + 0[bp]
+ push ax
+ ;; Set the magic number in ax and the boot drive in dl.
+ mov ax, #0xaa55
+ mov dl, _int19_function.bootdrv + 0[bp]
+ ;; Zero some of the other registers.
+ xor bx, bx
+ mov ds, bx
+ mov es, bx
+ mov bp, bx
+ ;; Go!
+ iret
+ASM_END
+}
+
+ void
+int1a_function(regs, ds, iret_addr)
+ pusha_regs_t regs; // regs pushed from PUSHA instruction
+ Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+ iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
+{
+ Bit8u val8;
+
+ BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n", regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds);
+
+ ASM_START
+ sti
+ ASM_END
+
+ switch (regs.u.r8.ah) {
+ case 0: // get current clock count
+ ASM_START
+ cli
+ ASM_END
+ regs.u.r16.cx = BiosData->ticks_high;
+ regs.u.r16.dx = BiosData->ticks_low;
+ regs.u.r8.al = BiosData->midnight_flag;
+ BiosData->midnight_flag = 0; // reset flag
+ ASM_START
+ sti
+ ASM_END
+ // AH already 0
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+ case 1: // Set Current Clock Count
+ ASM_START
+ cli
+ ASM_END
+ BiosData->ticks_high = regs.u.r16.cx;
+ BiosData->ticks_low = regs.u.r16.dx;
+ BiosData->midnight_flag = 0; // reset flag
+ ASM_START
+ sti
+ ASM_END
+ regs.u.r8.ah = 0;
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+
+ case 2: // Read CMOS Time
+ if (rtc_updating()) {
+ SetCF(iret_addr.flags);
+ break;
+ }
+
+ regs.u.r8.dh = inb_cmos(0x00); // Seconds
+ regs.u.r8.cl = inb_cmos(0x02); // Minutes
+ regs.u.r8.ch = inb_cmos(0x04); // Hours
+ regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
+ regs.u.r8.ah = 0;
+ regs.u.r8.al = regs.u.r8.ch;
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+ case 3: // Set CMOS Time
+ // Using a debugger, I notice the following masking/setting
+ // of bits in Status Register B, by setting Reg B to
+ // a few values and getting its value after INT 1A was called.
+ //
+ // try#1 try#2 try#3
+ // before 1111 1101 0111 1101 0000 0000
+ // after 0110 0010 0110 0010 0000 0010
+ //
+ // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+ // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
+ if (rtc_updating()) {
+ init_rtc();
+ // fall through as if an update were not in progress
+ }
+ outb_cmos(0x00, regs.u.r8.dh); // Seconds
+ outb_cmos(0x02, regs.u.r8.cl); // Minutes
+ outb_cmos(0x04, regs.u.r8.ch); // Hours
+ // Set Daylight Savings time enabled bit to requested value
+ val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
+ // (reg B already selected)
+ outb_cmos(0x0b, val8);
+ regs.u.r8.ah = 0;
+ regs.u.r8.al = val8; // val last written to Reg B
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+ case 4: // Read CMOS Date
+ regs.u.r8.ah = 0;
+ if (rtc_updating()) {
+ SetCF(iret_addr.flags);
+ break;
+ }
+ regs.u.r8.cl = inb_cmos(0x09); // Year
+ regs.u.r8.dh = inb_cmos(0x08); // Month
+ regs.u.r8.dl = inb_cmos(0x07); // Day of Month
+ regs.u.r8.ch = inb_cmos(0x32); // Century
+ regs.u.r8.al = regs.u.r8.ch;
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+ case 5: // Set CMOS Date
+ // Using a debugger, I notice the following masking/setting
+ // of bits in Status Register B, by setting Reg B to
+ // a few values and getting its value after INT 1A was called.
+ //
+ // try#1 try#2 try#3 try#4
+ // before 1111 1101 0111 1101 0000 0010 0000 0000
+ // after 0110 1101 0111 1101 0000 0010 0000 0000
+ //
+ // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+ // My assumption: RegB = (RegB & 01111111b)
+ if (rtc_updating()) {
+ init_rtc();
+ SetCF(iret_addr.flags);
+ break;
+ }
+ outb_cmos(0x09, regs.u.r8.cl); // Year
+ outb_cmos(0x08, regs.u.r8.dh); // Month
+ outb_cmos(0x07, regs.u.r8.dl); // Day of Month
+ outb_cmos(0x32, regs.u.r8.ch); // Century
+ val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
+ outb_cmos(0x0b, val8);
+ regs.u.r8.ah = 0;
+ regs.u.r8.al = val8; // AL = val last written to Reg B
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+ case 6: // Set Alarm Time in CMOS
+ // Using a debugger, I notice the following masking/setting
+ // of bits in Status Register B, by setting Reg B to
+ // a few values and getting its value after INT 1A was called.
+ //
+ // try#1 try#2 try#3
+ // before 1101 1111 0101 1111 0000 0000
+ // after 0110 1111 0111 1111 0010 0000
+ //
+ // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+ // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
+ val8 = inb_cmos(0x0b); // Get Status Reg B
+ regs.u.r16.ax = 0;
+ if (val8 & 0x20) {
+ // Alarm interrupt enabled already
+ SetCF(iret_addr.flags); // Error: alarm in use
+ break;
+ }
+ if (rtc_updating()) {
+ init_rtc();
+ // fall through as if an update were not in progress
+ }
+ outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
+ outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
+ outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
+ outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
+ // enable Status Reg B alarm bit, clear halt clock bit
+ outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
+ ClearCF(iret_addr.flags); // OK
+ break;
+
+ case 7: // Turn off Alarm
+ // Using a debugger, I notice the following masking/setting
+ // of bits in Status Register B, by setting Reg B to
+ // a few values and getting its value after INT 1A was called.
+ //
+ // try#1 try#2 try#3 try#4
+ // before 1111 1101 0111 1101 0010 0000 0010 0010
+ // after 0100 0101 0101 0101 0000 0000 0000 0010
+ //
+ // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+ // My assumption: RegB = (RegB & 01010111b)
+ val8 = inb_cmos(0x0b); // Get Status Reg B
+ // clear clock-halt bit, disable alarm bit
+ outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
+ regs.u.r8.ah = 0;
+ regs.u.r8.al = val8; // val last written to Reg B
+ ClearCF(iret_addr.flags); // OK
+ break;
+#if BX_PCIBIOS
+ case 0xb1:
+ // real mode PCI BIOS functions now handled in assembler code
+ // this C code handles the error code for information only
+ if (regs.u.r8.bl == 0xff) {
+ BX_INFO("PCI BIOS: PCI not present\n");
+ } else if (regs.u.r8.bl == 0x81) {
+ BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
+ } else if (regs.u.r8.bl == 0x83) {
+ BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
+ } else if (regs.u.r8.bl == 0x86) {
+ if (regs.u.r8.al == 0x02) {
+ BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
+ } else {
+ BX_INFO("no PCI device with class code 0x%02x%04x found at index %d\n", regs.u.r8.cl, regs.u.r16.dx, regs.u.r16.si);
+ }
+ }
+ regs.u.r8.ah = regs.u.r8.bl;
+ SetCF(iret_addr.flags);
+ break;
+#endif
+
+ default:
+ SetCF(iret_addr.flags); // Unsupported
+ }
+}
+
+ void
+int70_function(regs, ds, iret_addr)
+ pusha_regs_t regs; // regs pushed from PUSHA instruction
+ Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+ iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call
+{
+ // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
+ Bit8u registerB = 0, registerC = 0;
+
+ // Check which modes are enabled and have occurred.
+ registerB = inb_cmos( 0xB );
+ registerC = inb_cmos( 0xC );
+
+ if( ( registerB & 0x60 ) != 0 ) {
+ if( ( registerC & 0x20 ) != 0 ) {
+ // Handle Alarm Interrupt.
+ASM_START
+ sti
+ int #0x4a
+ cli
+ASM_END
+ }
+ if( ( registerC & 0x40 ) != 0 ) {
+ // Handle Periodic Interrupt.
+
+ if( read_byte( 0x40, 0xA0 ) != 0 ) {
+ // Wait Interval (Int 15, AH=83) active.
+ Bit32u time, toggle;
+
+ time = read_dword( 0x40, 0x9C ); // Time left in microseconds.
+ if( time < 0x3D1 ) {
+ // Done waiting.
+ Bit16u segment, offset;
+
+ segment = read_word( 0x40, 0x98 );
+ offset = read_word( 0x40, 0x9A );
+ write_byte( 0x40, 0xA0, 0 ); // Turn of status byte.
+ outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
+ write_byte(segment, offset, read_byte(segment, offset) | 0x80 ); // Write to specified flag byte.
+ } else {
+ // Continue waiting.
+ time -= 0x3D1;
+ write_dword( 0x40, 0x9C, time );
+ }
+ }
+ }
+ }
+
+ASM_START
+ call eoi_both_pics
+ASM_END
+}
+
+
+ASM_START
+;------------------------------------------
+;- INT74h : PS/2 mouse hardware interrupt -
+;------------------------------------------
+int74_handler:
+ sti
+ pusha
+ push ds ;; save DS
+ push #0x00 ;; placeholder for status
+ push #0x00 ;; placeholder for X
+ push #0x00 ;; placeholder for Y
+ push #0x00 ;; placeholder for Z
+ push #0x00 ;; placeholder for make_far_call boolean
+ call _int74_function
+ pop cx ;; remove make_far_call from stack
+ jcxz int74_done
+
+ ;; make far call to EBDA:0022
+ push #0x00
+ pop ds
+ push 0x040E ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
+ pop ds
+ //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
+ call far ptr[0x22]
+int74_done:
+ cli
+ call eoi_both_pics
+ add sp, #8 ;; pop status, x, y, z
+
+ pop ds ;; restore DS
+ popa
+ iret
+
+
+;; This will perform an IRET, but will retain value of current CF
+;; by altering flags on stack. Better than RETF #02.
+iret_modify_cf:
+ jc carry_set
+ push bp
+ mov bp, sp
+ and BYTE [bp + 0x06], #0xfe
+ pop bp
+ iret
+carry_set:
+ push bp
+ mov bp, sp
+ or BYTE [bp + 0x06], #0x01
+ pop bp
+ iret
+
+
+;----------------------
+;- INT13h (relocated) -
+;----------------------
+;
+; int13_relocated is a little bit messed up since I played with it
+; I have to rewrite it:
+; - call a function that detect which function to call
+; - make all called C function get the same parameters list
+;
+int13_relocated:
+
+#if BX_ELTORITO_BOOT
+ ;; check for an eltorito function
+ cmp ah,#0x4a
+ jb int13_not_eltorito
+ cmp ah,#0x4d
+ ja int13_not_eltorito
+
+ pusha
+ push es
+ push ds
+ push ss
+ pop ds
+
+ push #int13_out
+ jmp _int13_eltorito ;; ELDX not used
+
+int13_not_eltorito:
+ push ax
+ push bx
+ push cx
+ push dx
+
+ ;; check if emulation active
+ call _cdemu_isactive
+ cmp al,#0x00
+ je int13_cdemu_inactive
+
+ ;; check if access to the emulated drive
+ call _cdemu_emulated_drive
+ pop dx
+ push dx
+ cmp al,dl ;; int13 on emulated drive
+ jne int13_nocdemu
+
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+
+ pusha
+ push es
+ push ds
+ push ss
+ pop ds
+
+ push #int13_out
+ jmp _int13_cdemu ;; ELDX not used
+
+int13_nocdemu:
+ and dl,#0xE0 ;; mask to get device class, including cdroms
+ cmp al,dl ;; al is 0x00 or 0x80
+ jne int13_cdemu_inactive ;; inactive for device class
+
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+
+ push ax
+ push cx
+ push dx
+ push bx
+
+ dec dl ;; real drive is dl - 1
+ jmp int13_legacy
+
+int13_cdemu_inactive:
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+
+#endif // BX_ELTORITO_BOOT
+
+int13_noeltorito:
+
+ push ax
+ push cx
+ push dx
+ push bx
+
+int13_legacy:
+
+ push dx ;; push eltorito value of dx instead of sp
+
+ push bp
+ push si
+ push di
+
+ push es
+ push ds
+ push ss
+ pop ds
+
+ ;; now the 16-bit registers can be restored with:
+ ;; pop ds; pop es; popa; iret
+ ;; arguments passed to functions should be
+ ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
+
+ test dl, #0x80
+ jnz int13_notfloppy
+
+ push #int13_out
+ jmp _int13_diskette_function
+
+int13_notfloppy:
+
+#if BX_USE_ATADRV
+
+ cmp dl, #0xE0
+ jb int13_notcdrom
+
+ // ebx is modified: BSD 5.2.1 boot loader problem
+ // someone should figure out which 32 bit register that actually are used
+
+ shr ebx, #16
+ push bx
+
+ call _int13_cdrom
+
+ pop bx
+ shl ebx, #16
+
+ jmp int13_out
+
+int13_notcdrom:
+
+#endif
+
+int13_disk:
+ ;; int13_harddisk modifies high word of EAX
+ shr eax, #16
+ push ax
+ call _int13_harddisk
+ pop ax
+ shl eax, #16
+
+int13_out:
+ pop ds
+ pop es
+ popa
+ iret
+
+;----------
+;- INT18h -
+;----------
+int18_handler: ;; Boot Failure recovery: try the next device.
+
+ ;; Reset SP and SS
+ mov ax, #0xfffe
+ mov sp, ax
+ xor ax, ax
+ mov ss, ax
+
+ ;; Get the boot sequence number out of the IPL memory
+ mov bx, #IPL_SEG
+ mov ds, bx ;; Set segment
+ mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number
+ inc bx ;; ++
+ mov IPL_SEQUENCE_OFFSET, bx ;; Write it back
+ mov ds, ax ;; and reset the segment to zero.
+
+ ;; Carry on in the INT 19h handler, using the new sequence number
+ push bx
+
+ jmp int19_next_boot
+
+;----------
+;- INT19h -
+;----------
+int19_relocated: ;; Boot function, relocated
+
+ ;; int19 was beginning to be really complex, so now it
+ ;; just calls a C function that does the work
+
+ push bp
+ mov bp, sp
+
+ ;; Reset SS and SP
+ mov ax, #0xfffe
+ mov sp, ax
+ xor ax, ax
+ mov ss, ax
+
+ ;; Start from the first boot device (0, in AX)
+ mov bx, #IPL_SEG
+ mov ds, bx ;; Set segment to write to the IPL memory
+ mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number
+ mov ds, ax ;; and reset the segment.
+
+ push ax
+
+int19_next_boot:
+
+ ;; Call the C code for the next boot device
+ call _int19_function
+
+ ;; Boot failed: invoke the boot recovery function
+ int #0x18
+
+;----------
+;- INT1Ch -
+;----------
+int1c_handler: ;; User Timer Tick
+ iret
+
+
+;----------------------
+;- POST: Floppy Drive -
+;----------------------
+floppy_drive_post:
+ xor ax, ax
+ mov ds, ax
+
+ mov al, #0x00
+ mov 0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
+
+ mov 0x043f, al ;; diskette motor status: read op, drive0, motors off
+
+ mov 0x0440, al ;; diskette motor timeout counter: not active
+ mov 0x0441, al ;; diskette controller status return code
+
+ mov 0x0442, al ;; disk & diskette controller status register 0
+ mov 0x0443, al ;; diskette controller status register 1
+ mov 0x0444, al ;; diskette controller status register 2
+ mov 0x0445, al ;; diskette controller cylinder number
+ mov 0x0446, al ;; diskette controller head number
+ mov 0x0447, al ;; diskette controller sector number
+ mov 0x0448, al ;; diskette controller bytes written
+
+ mov 0x048b, al ;; diskette configuration data
+
+ ;; -----------------------------------------------------------------
+ ;; (048F) diskette controller information
+ ;;
+ mov al, #0x10 ;; get CMOS diskette drive type
+ out 0x70, AL
+ in AL, 0x71
+ mov ah, al ;; save byte to AH
+
+look_drive0:
+ shr al, #4 ;; look at top 4 bits for drive 0
+ jz f0_missing ;; jump if no drive0
+ mov bl, #0x07 ;; drive0 determined, multi-rate, has changed line
+ jmp look_drive1
+f0_missing:
+ mov bl, #0x00 ;; no drive0
+
+look_drive1:
+ mov al, ah ;; restore from AH
+ and al, #0x0f ;; look at bottom 4 bits for drive 1
+ jz f1_missing ;; jump if no drive1
+ or bl, #0x70 ;; drive1 determined, multi-rate, has changed line
+f1_missing:
+ ;; leave high bits in BL zerod
+ mov 0x048f, bl ;; put new val in BDA (diskette controller information)
+ ;; -----------------------------------------------------------------
+
+ mov al, #0x00
+ mov 0x0490, al ;; diskette 0 media state
+ mov 0x0491, al ;; diskette 1 media state
+
+ ;; diskette 0,1 operational starting state
+ ;; drive type has not been determined,
+ ;; has no changed detection line
+ mov 0x0492, al
+ mov 0x0493, al
+
+ mov 0x0494, al ;; diskette 0 current cylinder
+ mov 0x0495, al ;; diskette 1 current cylinder
+
+ mov al, #0x02
+ out #0x0a, al ;; clear DMA-1 channel 2 mask bit
+
+ SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
+ SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
+ SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
+
+ ret
+
+
+;--------------------
+;- POST: HARD DRIVE -
+;--------------------
+; relocated here because the primary POST area isnt big enough.
+hard_drive_post:
+ // IRQ 14 = INT 76h
+ // INT 76h calls INT 15h function ax=9100
+
+ mov al, #0x0a ; 0000 1010 = reserved, disable IRQ 14
+ mov dx, #0x03f6
+ out dx, al
+
+ xor ax, ax
+ mov ds, ax
+ mov 0x0474, al /* hard disk status of last operation */
+ mov 0x0477, al /* hard disk port offset (XT only ???) */
+ mov 0x048c, al /* hard disk status register */
+ mov 0x048d, al /* hard disk error register */
+ mov 0x048e, al /* hard disk task complete flag */
+ mov al, #0x01
+ mov 0x0475, al /* hard disk number attached */
+ mov al, #0xc0
+ mov 0x0476, al /* hard disk control byte */
+ SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
+ SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
+ ;; INT 41h: hard disk 0 configuration pointer
+ ;; INT 46h: hard disk 1 configuration pointer
+ SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
+ SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
+
+ ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
+ mov al, #0x12
+ out #0x70, al
+ in al, #0x71
+ and al, #0xf0
+ cmp al, #0xf0
+ je post_d0_extended
+ jmp check_for_hd1
+post_d0_extended:
+ mov al, #0x19
+ out #0x70, al
+ in al, #0x71
+ cmp al, #47 ;; decimal 47 - user definable
+ je post_d0_type47
+ HALT(__LINE__)
+post_d0_type47:
+ ;; CMOS purpose param table offset
+ ;; 1b cylinders low 0
+ ;; 1c cylinders high 1
+ ;; 1d heads 2
+ ;; 1e write pre-comp low 5
+ ;; 1f write pre-comp high 6
+ ;; 20 retries/bad map/heads>8 8
+ ;; 21 landing zone low C
+ ;; 22 landing zone high D
+ ;; 23 sectors/track E
+
+ mov ax, #EBDA_SEG
+ mov ds, ax
+
+ ;;; Filling EBDA table for hard disk 0.
+ mov al, #0x1f
+ out #0x70, al
+ in al, #0x71
+ mov ah, al
+ mov al, #0x1e
+ out #0x70, al
+ in al, #0x71
+ mov (0x003d + 0x05), ax ;; write precomp word
+
+ mov al, #0x20
+ out #0x70, al
+ in al, #0x71
+ mov (0x003d + 0x08), al ;; drive control byte
+
+ mov al, #0x22
+ out #0x70, al
+ in al, #0x71
+ mov ah, al
+ mov al, #0x21
+ out #0x70, al
+ in al, #0x71
+ mov (0x003d + 0x0C), ax ;; landing zone word
+
+ mov al, #0x1c ;; get cylinders word in AX
+ out #0x70, al
+ in al, #0x71 ;; high byte
+ mov ah, al
+ mov al, #0x1b
+ out #0x70, al
+ in al, #0x71 ;; low byte
+ mov bx, ax ;; BX = cylinders
+
+ mov al, #0x1d
+ out #0x70, al
+ in al, #0x71
+ mov cl, al ;; CL = heads
+
+ mov al, #0x23
+ out #0x70, al
+ in al, #0x71
+ mov dl, al ;; DL = sectors
+
+ cmp bx, #1024
+ jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
+
+hd0_post_physical_chs:
+ ;; no logical CHS mapping used, just physical CHS
+ ;; use Standard Fixed Disk Parameter Table (FDPT)
+ mov (0x003d + 0x00), bx ;; number of physical cylinders
+ mov (0x003d + 0x02), cl ;; number of physical heads
+ mov (0x003d + 0x0E), dl ;; number of physical sectors
+ jmp check_for_hd1
+
+hd0_post_logical_chs:
+ ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
+ mov (0x003d + 0x09), bx ;; number of physical cylinders
+ mov (0x003d + 0x0b), cl ;; number of physical heads
+ mov (0x003d + 0x04), dl ;; number of physical sectors
+ mov (0x003d + 0x0e), dl ;; number of logical sectors (same)
+ mov al, #0xa0
+ mov (0x003d + 0x03), al ;; A0h signature, indicates translated table
+
+ cmp bx, #2048
+ jnbe hd0_post_above_2048
+ ;; 1024 < c <= 2048 cylinders
+ shr bx, #0x01
+ shl cl, #0x01
+ jmp hd0_post_store_logical
+
+hd0_post_above_2048:
+ cmp bx, #4096
+ jnbe hd0_post_above_4096
+ ;; 2048 < c <= 4096 cylinders
+ shr bx, #0x02
+ shl cl, #0x02
+ jmp hd0_post_store_logical
+
+hd0_post_above_4096:
+ cmp bx, #8192
+ jnbe hd0_post_above_8192
+ ;; 4096 < c <= 8192 cylinders
+ shr bx, #0x03
+ shl cl, #0x03
+ jmp hd0_post_store_logical
+
+hd0_post_above_8192:
+ ;; 8192 < c <= 16384 cylinders
+ shr bx, #0x04
+ shl cl, #0x04
+
+hd0_post_store_logical:
+ mov (0x003d + 0x00), bx ;; number of physical cylinders
+ mov (0x003d + 0x02), cl ;; number of physical heads
+ ;; checksum
+ mov cl, #0x0f ;; repeat count
+ mov si, #0x003d ;; offset to disk0 FDPT
+ mov al, #0x00 ;; sum
+hd0_post_checksum_loop:
+ add al, [si]
+ inc si
+ dec cl
+ jnz hd0_post_checksum_loop
+ not al ;; now take 2s complement
+ inc al
+ mov [si], al
+;;; Done filling EBDA table for hard disk 0.
+
+
+check_for_hd1:
+ ;; is there really a second hard disk? if not, return now
+ mov al, #0x12
+ out #0x70, al
+ in al, #0x71
+ and al, #0x0f
+ jnz post_d1_exists
+ ret
+post_d1_exists:
+ ;; check that the hd type is really 0x0f.
+ cmp al, #0x0f
+ jz post_d1_extended
+ HALT(__LINE__)
+post_d1_extended:
+ ;; check that the extended type is 47 - user definable
+ mov al, #0x1a
+ out #0x70, al
+ in al, #0x71
+ cmp al, #47 ;; decimal 47 - user definable
+ je post_d1_type47
+ HALT(__LINE__)
+post_d1_type47:
+ ;; Table for disk1.
+ ;; CMOS purpose param table offset
+ ;; 0x24 cylinders low 0
+ ;; 0x25 cylinders high 1
+ ;; 0x26 heads 2
+ ;; 0x27 write pre-comp low 5
+ ;; 0x28 write pre-comp high 6
+ ;; 0x29 heads>8 8
+ ;; 0x2a landing zone low C
+ ;; 0x2b landing zone high D
+ ;; 0x2c sectors/track E
+;;; Fill EBDA table for hard disk 1.
+ mov ax, #EBDA_SEG
+ mov ds, ax
+ mov al, #0x28
+ out #0x70, al
+ in al, #0x71
+ mov ah, al
+ mov al, #0x27
+ out #0x70, al
+ in al, #0x71
+ mov (0x004d + 0x05), ax ;; write precomp word
+
+ mov al, #0x29
+ out #0x70, al
+ in al, #0x71
+ mov (0x004d + 0x08), al ;; drive control byte
+
+ mov al, #0x2b
+ out #0x70, al
+ in al, #0x71
+ mov ah, al
+ mov al, #0x2a
+ out #0x70, al
+ in al, #0x71
+ mov (0x004d + 0x0C), ax ;; landing zone word
+
+ mov al, #0x25 ;; get cylinders word in AX
+ out #0x70, al
+ in al, #0x71 ;; high byte
+ mov ah, al
+ mov al, #0x24
+ out #0x70, al
+ in al, #0x71 ;; low byte
+ mov bx, ax ;; BX = cylinders
+
+ mov al, #0x26
+ out #0x70, al
+ in al, #0x71
+ mov cl, al ;; CL = heads
+
+ mov al, #0x2c
+ out #0x70, al
+ in al, #0x71
+ mov dl, al ;; DL = sectors
+
+ cmp bx, #1024
+ jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
+
+hd1_post_physical_chs:
+ ;; no logical CHS mapping used, just physical CHS
+ ;; use Standard Fixed Disk Parameter Table (FDPT)
+ mov (0x004d + 0x00), bx ;; number of physical cylinders
+ mov (0x004d + 0x02), cl ;; number of physical heads
+ mov (0x004d + 0x0E), dl ;; number of physical sectors
+ ret
+
+hd1_post_logical_chs:
+ ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
+ mov (0x004d + 0x09), bx ;; number of physical cylinders
+ mov (0x004d + 0x0b), cl ;; number of physical heads
+ mov (0x004d + 0x04), dl ;; number of physical sectors
+ mov (0x004d + 0x0e), dl ;; number of logical sectors (same)
+ mov al, #0xa0
+ mov (0x004d + 0x03), al ;; A0h signature, indicates translated table
+
+ cmp bx, #2048
+ jnbe hd1_post_above_2048
+ ;; 1024 < c <= 2048 cylinders
+ shr bx, #0x01
+ shl cl, #0x01
+ jmp hd1_post_store_logical
+
+hd1_post_above_2048:
+ cmp bx, #4096
+ jnbe hd1_post_above_4096
+ ;; 2048 < c <= 4096 cylinders
+ shr bx, #0x02
+ shl cl, #0x02
+ jmp hd1_post_store_logical
+
+hd1_post_above_4096:
+ cmp bx, #8192
+ jnbe hd1_post_above_8192
+ ;; 4096 < c <= 8192 cylinders
+ shr bx, #0x03
+ shl cl, #0x03
+ jmp hd1_post_store_logical
+
+hd1_post_above_8192:
+ ;; 8192 < c <= 16384 cylinders
+ shr bx, #0x04
+ shl cl, #0x04
+
+hd1_post_store_logical:
+ mov (0x004d + 0x00), bx ;; number of physical cylinders
+ mov (0x004d + 0x02), cl ;; number of physical heads
+ ;; checksum
+ mov cl, #0x0f ;; repeat count
+ mov si, #0x004d ;; offset to disk0 FDPT
+ mov al, #0x00 ;; sum
+hd1_post_checksum_loop:
+ add al, [si]
+ inc si
+ dec cl
+ jnz hd1_post_checksum_loop
+ not al ;; now take 2s complement
+ inc al
+ mov [si], al
+;;; Done filling EBDA table for hard disk 1.
+
+ ret
+
+;--------------------
+;- POST: EBDA segment
+;--------------------
+; relocated here because the primary POST area isnt big enough.
+ebda_post:
+#if BX_USE_EBDA
+ mov ax, #EBDA_SEG
+ mov ds, ax
+ mov byte ptr [0x0], #EBDA_SIZE
+#endif
+ xor ax, ax ; mov EBDA seg into 40E
+ mov ds, ax
+ mov word ptr [0x40E], #EBDA_SEG
+ ret;;
+
+;--------------------
+;- POST: EOI + jmp via [0x40:67)
+;--------------------
+; relocated here because the primary POST area isnt big enough.
+eoi_jmp_post:
+ mov al, #0x20
+ out #0xA0, al ;; slave PIC EOI
+ mov al, #0x20
+ out #0x20, al ;; master PIC EOI
+
+jmp_post_0x467:
+ xor ax, ax
+ mov ds, ax
+
+ jmp far ptr [0x467]
+
+iret_post_0x467:
+ xor ax, ax
+ mov ds, ax
+
+ mov sp, [0x467]
+ mov ss, [0x469]
+ iret
+
+retf_post_0x467:
+ xor ax, ax
+ mov ds, ax
+
+ mov sp, [0x467]
+ mov ss, [0x469]
+ retf
+
+s3_post:
+ mov sp, #0xffe
+#if BX_ROMBIOS32
+ call rombios32_init
+#endif
+ call _s3_resume
+ mov bl, #0x00
+ and ax, ax
+ jz normal_post
+ call _s3_resume_panic
+
+;--------------------
+eoi_both_pics:
+ mov al, #0x20
+ out #0xA0, al ;; slave PIC EOI
+eoi_master_pic:
+ mov al, #0x20
+ out #0x20, al ;; master PIC EOI
+ ret
+
+;--------------------
+BcdToBin:
+ ;; in: AL in BCD format
+ ;; out: AL in binary format, AH will always be 0
+ ;; trashes BX
+ mov bl, al
+ and bl, #0x0f ;; bl has low digit
+ shr al, #4 ;; al has high digit
+ mov bh, #10
+ mul al, bh ;; multiply high digit by 10 (result in AX)
+ add al, bl ;; then add low digit
+ ret
+
+;--------------------
+timer_tick_post:
+ ;; Setup the Timer Ticks Count (0x46C:dword) and
+ ;; Timer Ticks Roller Flag (0x470:byte)
+ ;; The Timer Ticks Count needs to be set according to
+ ;; the current CMOS time, as if ticks have been occurring
+ ;; at 18.2hz since midnight up to this point. Calculating
+ ;; this is a little complicated. Here are the factors I gather
+ ;; regarding this. 14,318,180 hz was the original clock speed,
+ ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
+ ;; at the time, or 4 to drive the CGA video adapter. The div3
+ ;; source was divided again by 4 to feed a 1.193Mhz signal to
+ ;; the timer. With a maximum 16bit timer count, this is again
+ ;; divided down by 65536 to 18.2hz.
+ ;;
+ ;; 14,318,180 Hz clock
+ ;; /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
+ ;; /4 = 1,193,181 Hz fed to timer
+ ;; /65536 (maximum timer count) = 18.20650736 ticks/second
+ ;; 1 second = 18.20650736 ticks
+ ;; 1 minute = 1092.390442 ticks
+ ;; 1 hour = 65543.42651 ticks
+ ;;
+ ;; Given the values in the CMOS clock, one could calculate
+ ;; the number of ticks by the following:
+ ;; ticks = (BcdToBin(seconds) * 18.206507) +
+ ;; (BcdToBin(minutes) * 1092.3904)
+ ;; (BcdToBin(hours) * 65543.427)
+ ;; To get a little more accuracy, since Im using integer
+ ;; arithmatic, I use:
+ ;; ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
+ ;; (BcdToBin(minutes) * 10923904) / 10000 +
+ ;; (BcdToBin(hours) * 65543427) / 1000
+
+ ;; assuming DS=0000
+
+ ;; get CMOS seconds
+ xor eax, eax ;; clear EAX
+ mov al, #0x00
+ out #0x70, al
+ in al, #0x71 ;; AL has CMOS seconds in BCD
+ call BcdToBin ;; EAX now has seconds in binary
+ mov edx, #18206507
+ mul eax, edx
+ mov ebx, #1000000
+ xor edx, edx
+ div eax, ebx
+ mov ecx, eax ;; ECX will accumulate total ticks
+
+ ;; get CMOS minutes
+ xor eax, eax ;; clear EAX
+ mov al, #0x02
+ out #0x70, al
+ in al, #0x71 ;; AL has CMOS minutes in BCD
+ call BcdToBin ;; EAX now has minutes in binary
+ mov edx, #10923904
+ mul eax, edx
+ mov ebx, #10000
+ xor edx, edx
+ div eax, ebx
+ add ecx, eax ;; add to total ticks
+
+ ;; get CMOS hours
+ xor eax, eax ;; clear EAX
+ mov al, #0x04
+ out #0x70, al
+ in al, #0x71 ;; AL has CMOS hours in BCD
+ call BcdToBin ;; EAX now has hours in binary
+ mov edx, #65543427
+ mul eax, edx
+ mov ebx, #1000
+ xor edx, edx
+ div eax, ebx
+ add ecx, eax ;; add to total ticks
+
+ mov 0x46C, ecx ;; Timer Ticks Count
+ xor al, al
+ mov 0x470, al ;; Timer Ticks Rollover Flag
+ ret
+
+;--------------------
+int76_handler:
+ ;; record completion in BIOS task complete flag
+ push ax
+ push ds
+ mov ax, #0x0040
+ mov ds, ax
+ mov 0x008E, #0xff
+ call eoi_both_pics
+ pop ds
+ pop ax
+ iret
+
+
+;--------------------
+#if BX_APM
+
+use32 386
+#define APM_PROT32
+#include "apmbios.S"
+
+use16 386
+#define APM_PROT16
+#include "apmbios.S"
+
+#define APM_REAL
+#include "apmbios.S"
+
+#endif
+
+;--------------------
+#if BX_PCIBIOS
+use32 386
+.align 16
+bios32_structure:
+ db 0x5f, 0x33, 0x32, 0x5f ;; "_32_" signature
+ dw bios32_entry_point, 0xf ;; 32 bit physical address
+ db 0 ;; revision level
+ ;; length in paragraphs and checksum stored in a word to prevent errors
+ dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
+ & 0xff) << 8) + 0x01
+ db 0,0,0,0,0 ;; reserved
+
+.align 16
+bios32_entry_point:
+ pushfd
+ cmp eax, #0x49435024 ;; "$PCI"
+ jne unknown_service
+ mov eax, #0x80000000
+ mov dx, #0x0cf8
+ out dx, eax
+ mov dx, #0x0cfc
+ in eax, dx
+#ifdef PCI_FIXED_HOST_BRIDGE
+ cmp eax, #PCI_FIXED_HOST_BRIDGE
+ jne unknown_service
+#else
+ ;; say ok if a device is present
+ cmp eax, #0xffffffff
+ je unknown_service
+#endif
+ mov ebx, #0x000f0000
+ mov ecx, #0
+ mov edx, #pcibios_protected
+ xor al, al
+ jmp bios32_end
+unknown_service:
+ mov al, #0x80
+bios32_end:
+#ifdef BX_QEMU
+ and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
+#endif
+ popfd
+ retf
+
+.align 16
+pcibios_protected:
+ pushfd
+ cli
+ push esi
+ push edi
+ cmp al, #0x01 ;; installation check
+ jne pci_pro_f02
+ mov bx, #0x0210
+ mov cx, #0
+ mov edx, #0x20494350 ;; "PCI "
+ mov al, #0x01
+ jmp pci_pro_ok
+pci_pro_f02: ;; find pci device
+ cmp al, #0x02
+ jne pci_pro_f03
+ shl ecx, #16
+ mov cx, dx
+ xor bx, bx
+ mov di, #0x00
+pci_pro_devloop:
+ call pci_pro_select_reg
+ mov dx, #0x0cfc
+ in eax, dx
+ cmp eax, ecx
+ jne pci_pro_nextdev
+ cmp si, #0
+ je pci_pro_ok
+ dec si
+pci_pro_nextdev:
+ inc bx
+ cmp bx, #0x0100
+ jne pci_pro_devloop
+ mov ah, #0x86
+ jmp pci_pro_fail
+pci_pro_f03: ;; find class code
+ cmp al, #0x03
+ jne pci_pro_f08
+ xor bx, bx
+ mov di, #0x08
+pci_pro_devloop2:
+ call pci_pro_select_reg
+ mov dx, #0x0cfc
+ in eax, dx
+ shr eax, #8
+ cmp eax, ecx
+ jne pci_pro_nextdev2
+ cmp si, #0
+ je pci_pro_ok
+ dec si
+pci_pro_nextdev2:
+ inc bx
+ cmp bx, #0x0100
+ jne pci_pro_devloop2
+ mov ah, #0x86
+ jmp pci_pro_fail
+pci_pro_f08: ;; read configuration byte
+ cmp al, #0x08
+ jne pci_pro_f09
+ call pci_pro_select_reg
+ push edx
+ mov dx, di
+ and dx, #0x03
+ add dx, #0x0cfc
+ in al, dx
+ pop edx
+ mov cl, al
+ jmp pci_pro_ok
+pci_pro_f09: ;; read configuration word
+ cmp al, #0x09
+ jne pci_pro_f0a
+ call pci_pro_select_reg
+ push edx
+ mov dx, di
+ and dx, #0x02
+ add dx, #0x0cfc
+ in ax, dx
+ pop edx
+ mov cx, ax
+ jmp pci_pro_ok
+pci_pro_f0a: ;; read configuration dword
+ cmp al, #0x0a
+ jne pci_pro_f0b
+ call pci_pro_select_reg
+ push edx
+ mov dx, #0x0cfc
+ in eax, dx
+ pop edx
+ mov ecx, eax
+ jmp pci_pro_ok
+pci_pro_f0b: ;; write configuration byte
+ cmp al, #0x0b
+ jne pci_pro_f0c
+ call pci_pro_select_reg
+ push edx
+ mov dx, di
+ and dx, #0x03
+ add dx, #0x0cfc
+ mov al, cl
+ out dx, al
+ pop edx
+ jmp pci_pro_ok
+pci_pro_f0c: ;; write configuration word
+ cmp al, #0x0c
+ jne pci_pro_f0d
+ call pci_pro_select_reg
+ push edx
+ mov dx, di
+ and dx, #0x02
+ add dx, #0x0cfc
+ mov ax, cx
+ out dx, ax
+ pop edx
+ jmp pci_pro_ok
+pci_pro_f0d: ;; write configuration dword
+ cmp al, #0x0d
+ jne pci_pro_unknown
+ call pci_pro_select_reg
+ push edx
+ mov dx, #0x0cfc
+ mov eax, ecx
+ out dx, eax
+ pop edx
+ jmp pci_pro_ok
+pci_pro_unknown:
+ mov ah, #0x81
+pci_pro_fail:
+ pop edi
+ pop esi
+#ifdef BX_QEMU
+ and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
+#endif
+ popfd
+ stc
+ retf
+pci_pro_ok:
+ xor ah, ah
+ pop edi
+ pop esi
+#ifdef BX_QEMU
+ and dword ptr[esp+8],0xfffffffc ;; reset CS.RPL for kqemu
+#endif
+ popfd
+ clc
+ retf
+
+pci_pro_select_reg:
+ push edx
+ mov eax, #0x800000
+ mov ax, bx
+ shl eax, #8
+ and di, #0xff
+ or ax, di
+ and al, #0xfc
+ mov dx, #0x0cf8
+ out dx, eax
+ pop edx
+ ret
+
+use16 386
+
+pcibios_real:
+ push eax
+ push dx
+ mov eax, #0x80000000
+ mov dx, #0x0cf8
+ out dx, eax
+ mov dx, #0x0cfc
+ in eax, dx
+#ifdef PCI_FIXED_HOST_BRIDGE
+ cmp eax, #PCI_FIXED_HOST_BRIDGE
+ je pci_present
+#else
+ ;; say ok if a device is present
+ cmp eax, #0xffffffff
+ jne pci_present
+#endif
+ pop dx
+ pop eax
+ mov ah, #0xff
+ stc
+ ret
+pci_present:
+ pop dx
+ pop eax
+ cmp al, #0x01 ;; installation check
+ jne pci_real_f02
+ mov ax, #0x0001
+ mov bx, #0x0210
+ mov cx, #0
+ mov edx, #0x20494350 ;; "PCI "
+ mov edi, #0xf0000
+ mov di, #pcibios_protected
+ clc
+ ret
+pci_real_f02: ;; find pci device
+ push esi
+ push edi
+ cmp al, #0x02
+ jne pci_real_f03
+ shl ecx, #16
+ mov cx, dx
+ xor bx, bx
+ mov di, #0x00
+pci_real_devloop:
+ call pci_real_select_reg
+ mov dx, #0x0cfc
+ in eax, dx
+ cmp eax, ecx
+ jne pci_real_nextdev
+ cmp si, #0
+ je pci_real_ok
+ dec si
+pci_real_nextdev:
+ inc bx
+ cmp bx, #0x0100
+ jne pci_real_devloop
+ mov dx, cx
+ shr ecx, #16
+ mov ax, #0x8602
+ jmp pci_real_fail
+pci_real_f03: ;; find class code
+ cmp al, #0x03
+ jne pci_real_f08
+ xor bx, bx
+ mov di, #0x08
+pci_real_devloop2:
+ call pci_real_select_reg
+ mov dx, #0x0cfc
+ in eax, dx
+ shr eax, #8
+ cmp eax, ecx
+ jne pci_real_nextdev2
+ cmp si, #0
+ je pci_real_ok
+ dec si
+pci_real_nextdev2:
+ inc bx
+ cmp bx, #0x0100
+ jne pci_real_devloop2
+ mov dx, cx
+ shr ecx, #16
+ mov ax, #0x8603
+ jmp pci_real_fail
+pci_real_f08: ;; read configuration byte
+ cmp al, #0x08
+ jne pci_real_f09
+ call pci_real_select_reg
+ push dx
+ mov dx, di
+ and dx, #0x03
+ add dx, #0x0cfc
+ in al, dx
+ pop dx
+ mov cl, al
+ jmp pci_real_ok
+pci_real_f09: ;; read configuration word
+ cmp al, #0x09
+ jne pci_real_f0a
+ call pci_real_select_reg
+ push dx
+ mov dx, di
+ and dx, #0x02
+ add dx, #0x0cfc
+ in ax, dx
+ pop dx
+ mov cx, ax
+ jmp pci_real_ok
+pci_real_f0a: ;; read configuration dword
+ cmp al, #0x0a
+ jne pci_real_f0b
+ call pci_real_select_reg
+ push dx
+ mov dx, #0x0cfc
+ in eax, dx
+ pop dx
+ mov ecx, eax
+ jmp pci_real_ok
+pci_real_f0b: ;; write configuration byte
+ cmp al, #0x0b
+ jne pci_real_f0c
+ call pci_real_select_reg
+ push dx
+ mov dx, di
+ and dx, #0x03
+ add dx, #0x0cfc
+ mov al, cl
+ out dx, al
+ pop dx
+ jmp pci_real_ok
+pci_real_f0c: ;; write configuration word
+ cmp al, #0x0c
+ jne pci_real_f0d
+ call pci_real_select_reg
+ push dx
+ mov dx, di
+ and dx, #0x02
+ add dx, #0x0cfc
+ mov ax, cx
+ out dx, ax
+ pop dx
+ jmp pci_real_ok
+pci_real_f0d: ;; write configuration dword
+ cmp al, #0x0d
+ jne pci_real_f0e
+ call pci_real_select_reg
+ push dx
+ mov dx, #0x0cfc
+ mov eax, ecx
+ out dx, eax
+ pop dx
+ jmp pci_real_ok
+pci_real_f0e: ;; get irq routing options
+ cmp al, #0x0e
+ jne pci_real_unknown
+ SEG ES
+ cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
+ jb pci_real_too_small
+ SEG ES
+ mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
+ pushf
+ push ds
+ push es
+ push cx
+ push si
+ push di
+ cld
+ mov si, #pci_routing_table_structure_start
+ push cs
+ pop ds
+ SEG ES
+ mov cx, [di+2]
+ SEG ES
+ mov es, [di+4]
+ mov di, cx
+ mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
+ rep
+ movsb
+ pop di
+ pop si
+ pop cx
+ pop es
+ pop ds
+ popf
+ mov bx, #(1 << 9) | (1 << 11) ;; irq 9 and 11 are used
+ jmp pci_real_ok
+pci_real_too_small:
+ SEG ES
+ mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
+ mov ah, #0x89
+ jmp pci_real_fail
+
+pci_real_unknown:
+ mov ah, #0x81
+pci_real_fail:
+ pop edi
+ pop esi
+ stc
+ ret
+pci_real_ok:
+ xor ah, ah
+ pop edi
+ pop esi
+ clc
+ ret
+
+pci_real_select_reg:
+ push dx
+ mov eax, #0x800000
+ mov ax, bx
+ shl eax, #8
+ and di, #0xff
+ or ax, di
+ and al, #0xfc
+ mov dx, #0x0cf8
+ out dx, eax
+ pop dx
+ ret
+
+.align 16
+pci_routing_table_structure:
+ db 0x24, 0x50, 0x49, 0x52 ;; "$PIR" signature
+ db 0, 1 ;; version
+ dw 32 + (6 * 16) ;; table size
+ db 0 ;; PCI interrupt router bus
+ db 0x08 ;; PCI interrupt router DevFunc
+ dw 0x0000 ;; PCI exclusive IRQs
+ dw 0x8086 ;; compatible PCI interrupt router vendor ID
+ dw 0x122e ;; compatible PCI interrupt router device ID
+ dw 0,0 ;; Miniport data
+ db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
+ db 0x37 ;; checksum
+pci_routing_table_structure_start:
+ ;; first slot entry PCI-to-ISA (embedded)
+ db 0 ;; pci bus number
+ db 0x08 ;; pci device number (bit 7-3)
+ db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
+ dw 0xdef8 ;; IRQ bitmap INTA#
+ db 0x61 ;; link value INTB#
+ dw 0xdef8 ;; IRQ bitmap INTB#
+ db 0x62 ;; link value INTC#
+ dw 0xdef8 ;; IRQ bitmap INTC#
+ db 0x63 ;; link value INTD#
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 0 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
+ ;; second slot entry: 1st PCI slot
+ db 0 ;; pci bus number
+ db 0x10 ;; pci device number (bit 7-3)
+ db 0x61 ;; link value INTA#
+ dw 0xdef8 ;; IRQ bitmap INTA#
+ db 0x62 ;; link value INTB#
+ dw 0xdef8 ;; IRQ bitmap INTB#
+ db 0x63 ;; link value INTC#
+ dw 0xdef8 ;; IRQ bitmap INTC#
+ db 0x60 ;; link value INTD#
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 1 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
+ ;; third slot entry: 2nd PCI slot
+ db 0 ;; pci bus number
+ db 0x18 ;; pci device number (bit 7-3)
+ db 0x62 ;; link value INTA#
+ dw 0xdef8 ;; IRQ bitmap INTA#
+ db 0x63 ;; link value INTB#
+ dw 0xdef8 ;; IRQ bitmap INTB#
+ db 0x60 ;; link value INTC#
+ dw 0xdef8 ;; IRQ bitmap INTC#
+ db 0x61 ;; link value INTD#
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 2 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
+ ;; 4th slot entry: 3rd PCI slot
+ db 0 ;; pci bus number
+ db 0x20 ;; pci device number (bit 7-3)
+ db 0x63 ;; link value INTA#
+ dw 0xdef8 ;; IRQ bitmap INTA#
+ db 0x60 ;; link value INTB#
+ dw 0xdef8 ;; IRQ bitmap INTB#
+ db 0x61 ;; link value INTC#
+ dw 0xdef8 ;; IRQ bitmap INTC#
+ db 0x62 ;; link value INTD#
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 3 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
+ ;; 5th slot entry: 4rd PCI slot
+ db 0 ;; pci bus number
+ db 0x28 ;; pci device number (bit 7-3)
+ db 0x60 ;; link value INTA#
+ dw 0xdef8 ;; IRQ bitmap INTA#
+ db 0x61 ;; link value INTB#
+ dw 0xdef8 ;; IRQ bitmap INTB#
+ db 0x62 ;; link value INTC#
+ dw 0xdef8 ;; IRQ bitmap INTC#
+ db 0x63 ;; link value INTD#
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 4 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
+ ;; 6th slot entry: 5rd PCI slot
+ db 0 ;; pci bus number
+ db 0x30 ;; pci device number (bit 7-3)
+ db 0x61 ;; link value INTA#
+ dw 0xdef8 ;; IRQ bitmap INTA#
+ db 0x62 ;; link value INTB#
+ dw 0xdef8 ;; IRQ bitmap INTB#
+ db 0x63 ;; link value INTC#
+ dw 0xdef8 ;; IRQ bitmap INTC#
+ db 0x60 ;; link value INTD#
+ dw 0xdef8 ;; IRQ bitmap INTD#
+ db 5 ;; physical slot (0 = embedded)
+ db 0 ;; reserved
+pci_routing_table_structure_end:
+
+#if !BX_ROMBIOS32
+pci_irq_list:
+ db 11, 10, 9, 5;
+
+pcibios_init_sel_reg:
+ push eax
+ mov eax, #0x800000
+ mov ax, bx
+ shl eax, #8
+ and dl, #0xfc
+ or al, dl
+ mov dx, #0x0cf8
+ out dx, eax
+ pop eax
+ ret
+
+pcibios_init_iomem_bases:
+ push bp
+ mov bp, sp
+ mov eax, #0xe0000000 ;; base for memory init
+ push eax
+ mov ax, #0xc000 ;; base for i/o init
+ push ax
+ mov ax, #0x0010 ;; start at base address #0
+ push ax
+ mov bx, #0x0008
+pci_init_io_loop1:
+ mov dl, #0x00
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ in ax, dx
+ cmp ax, #0xffff
+ jz next_pci_dev
+ mov dl, #0x04 ;; disable i/o and memory space access
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ in al, dx
+ and al, #0xfc
+ out dx, al
+pci_init_io_loop2:
+ mov dl, [bp-8]
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ in eax, dx
+ test al, #0x01
+ jnz init_io_base
+ mov ecx, eax
+ mov eax, #0xffffffff
+ out dx, eax
+ in eax, dx
+ cmp eax, ecx
+ je next_pci_base
+ xor eax, #0xffffffff
+ mov ecx, eax
+ mov eax, [bp-4]
+ out dx, eax
+ add eax, ecx ;; calculate next free mem base
+ add eax, #0x01000000
+ and eax, #0xff000000
+ mov [bp-4], eax
+ jmp next_pci_base
+init_io_base:
+ mov cx, ax
+ mov ax, #0xffff
+ out dx, ax
+ in ax, dx
+ cmp ax, cx
+ je next_pci_base
+ xor ax, #0xfffe
+ mov cx, ax
+ mov ax, [bp-6]
+ out dx, ax
+ add ax, cx ;; calculate next free i/o base
+ add ax, #0x0100
+ and ax, #0xff00
+ mov [bp-6], ax
+next_pci_base:
+ mov al, [bp-8]
+ add al, #0x04
+ cmp al, #0x28
+ je enable_iomem_space
+ mov byte ptr[bp-8], al
+ jmp pci_init_io_loop2
+enable_iomem_space:
+ mov dl, #0x04 ;; enable i/o and memory space access if available
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ in al, dx
+ or al, #0x07
+ out dx, al
+next_pci_dev:
+ mov byte ptr[bp-8], #0x10
+ inc bx
+ cmp bx, #0x0100
+ jne pci_init_io_loop1
+ mov sp, bp
+ pop bp
+ ret
+
+pcibios_init_set_elcr:
+ push ax
+ push cx
+ mov dx, #0x04d0
+ test al, #0x08
+ jz is_master_pic
+ inc dx
+ and al, #0x07
+is_master_pic:
+ mov cl, al
+ mov bl, #0x01
+ shl bl, cl
+ in al, dx
+ or al, bl
+ out dx, al
+ pop cx
+ pop ax
+ ret
+
+pcibios_init_irqs:
+ push ds
+ push bp
+ mov ax, #0xf000
+ mov ds, ax
+ mov dx, #0x04d0 ;; reset ELCR1 + ELCR2
+ mov al, #0x00
+ out dx, al
+ inc dx
+ out dx, al
+ mov si, #pci_routing_table_structure
+ mov bh, [si+8]
+ mov bl, [si+9]
+ mov dl, #0x00
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ in 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
+ add dx, #2
+ out dx, ax
+ mov ax, [si+6]
+ sub ax, #0x20
+ shr ax, #4
+ mov cx, ax
+ add si, #0x20 ;; set pointer to 1st entry
+ mov bp, sp
+ mov ax, #pci_irq_list
+ push ax
+ xor ax, ax
+ push ax
+pci_init_irq_loop1:
+ mov bh, [si]
+ mov bl, [si+1]
+pci_init_irq_loop2:
+ mov dl, #0x00
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ in ax, dx
+ cmp ax, #0xffff
+ jnz pci_test_int_pin
+ test bl, #0x07
+ jz next_pir_entry
+ jmp next_pci_func
+pci_test_int_pin:
+ mov dl, #0x3c
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfd
+ in al, dx
+ and al, #0x07
+ jz next_pci_func
+ dec al ;; determine pirq reg
+ mov dl, #0x03
+ mul al, dl
+ add al, #0x02
+ xor ah, ah
+ mov bx, ax
+ mov al, [si+bx]
+ mov dl, al
+ mov bx, [bp]
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ and al, #0x03
+ add dl, al
+ in al, dx
+ cmp al, #0x80
+ jb pirq_found
+ mov bx, [bp-2] ;; pci irq list pointer
+ mov al, [bx]
+ out dx, al
+ inc bx
+ mov [bp-2], bx
+ call pcibios_init_set_elcr
+pirq_found:
+ mov bh, [si]
+ mov bl, [si+1]
+ add bl, [bp-3] ;; pci function number
+ mov dl, #0x3c
+ call pcibios_init_sel_reg
+ mov dx, #0x0cfc
+ out dx, al
+next_pci_func:
+ inc byte ptr[bp-3]
+ inc bl
+ test bl, #0x07
+ jnz pci_init_irq_loop2
+next_pir_entry:
+ add si, #0x10
+ mov byte ptr[bp-3], #0x00
+ loop pci_init_irq_loop1
+ mov sp, bp
+ pop bx
+pci_init_end:
+ pop bp
+ pop ds
+ ret
+#endif // !BX_ROMBIOS32
+#endif // BX_PCIBIOS
+
+#if BX_ROMBIOS32
+rombios32_init:
+ ;; save a20 and enable it
+ in al, 0x92
+ push ax
+ or al, #0x02
+ out 0x92, al
+
+ ;; save SS:SP to the BDA
+ xor ax, ax
+ mov ds, ax
+ mov 0x0469, ss
+ mov 0x0467, sp
+
+ SEG CS
+ lidt [pmode_IDT_info]
+ SEG CS
+ lgdt [rombios32_gdt_48]
+ ;; set PE bit in CR0
+ mov eax, cr0
+ or al, #0x01
+ mov cr0, eax
+ ;; start protected mode code: ljmpl 0x10:rombios32_init1
+ db 0x66, 0xea
+ dw rombios32_05
+ dw 0x000f ;; high 16 bit address
+ dw 0x0010
+
+use32 386
+rombios32_05:
+ ;; init data segments
+ mov eax, #0x18
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ xor eax, eax
+ mov fs, ax
+ mov gs, ax
+ cld
+
+ ;; init the stack pointer to point below EBDA
+ mov ax, [0x040e]
+ shl eax, #4
+ mov esp, #-0x10
+ add esp, eax
+
+ ;; pass pointer to s3_resume_flag and s3_resume_vector to rombios32
+ push #0x04b0
+ push #0x04b2
+
+ ;; call rombios32 code
+ mov eax, #0x000e0000
+ call eax
+
+ ;; return to 16 bit protected mode first
+ db 0xea
+ dd rombios32_10
+ dw 0x20
+
+use16 386
+rombios32_10:
+ ;; restore data segment limits to 0xffff
+ mov ax, #0x28
+ mov ds, ax
+ mov es, ax
+ mov ss, ax
+ mov fs, ax
+ mov gs, ax
+
+ ;; reset PE bit in CR0
+ mov eax, cr0
+ and al, #0xFE
+ mov cr0, eax
+
+ ;; far jump to flush CPU queue after transition to real mode
+ JMP_AP(0xf000, rombios32_real_mode)
+
+rombios32_real_mode:
+ ;; restore IDT to normal real-mode defaults
+ SEG CS
+ lidt [rmode_IDT_info]
+
+ xor ax, ax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ;; restore SS:SP from the BDA
+ mov ss, 0x0469
+ xor esp, esp
+ mov sp, 0x0467
+ ;; restore a20
+ pop ax
+ out 0x92, al
+ ret
+
+rombios32_gdt_48:
+ dw 0x30
+ dw rombios32_gdt
+ dw 0x000f
+
+rombios32_gdt:
+ dw 0, 0, 0, 0
+ dw 0, 0, 0, 0
+ dw 0xffff, 0, 0x9b00, 0x00cf ; 32 bit flat code segment (0x10)
+ dw 0xffff, 0, 0x9300, 0x00cf ; 32 bit flat data segment (0x18)
+ dw 0xffff, 0, 0x9b0f, 0x0000 ; 16 bit code segment base=0xf0000 limit=0xffff
+ dw 0xffff, 0, 0x9300, 0x0000 ; 16 bit data segment base=0x0 limit=0xffff
+#endif // BX_ROMBIOS32
+
+
+; parallel port detection: base address in DX, index in BX, timeout in CL
+detect_parport:
+ push dx
+ add dx, #2
+ in al, dx
+ and al, #0xdf ; clear input mode
+ out dx, al
+ pop dx
+ mov al, #0xaa
+ out dx, al
+ in al, dx
+ cmp al, #0xaa
+ jne no_parport
+ push bx
+ shl bx, #1
+ mov [bx+0x408], dx ; Parallel I/O address
+ pop bx
+ mov [bx+0x478], cl ; Parallel printer timeout
+ inc bx
+no_parport:
+ ret
+
+; serial port detection: base address in DX, index in BX, timeout in CL
+detect_serial:
+ push dx
+ inc dx
+ mov al, #0x02
+ out dx, al
+ in al, dx
+ cmp al, #0x02
+ jne no_serial
+ inc dx
+ in al, dx
+ cmp al, #0x02
+ jne no_serial
+ dec dx
+ xor al, al
+ out dx, al
+ pop dx
+ push bx
+ shl bx, #1
+ mov [bx+0x400], dx ; Serial I/O address
+ pop bx
+ mov [bx+0x47c], cl ; Serial timeout
+ inc bx
+ ret
+no_serial:
+ pop dx
+ ret
+
+rom_checksum:
+ push ax
+ push bx
+ push cx
+ xor ax, ax
+ xor bx, bx
+ xor cx, cx
+ mov ch, [2]
+ shl cx, #1
+checksum_loop:
+ add al, [bx]
+ inc bx
+ loop checksum_loop
+ and al, #0xff
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+
+;; We need a copy of this string, but we are not actually a PnP BIOS,
+;; so make sure it is *not* aligned, so OSes will not see it if they scan.
+.align 16
+ db 0
+pnp_string:
+ .ascii "$PnP"
+
+
+rom_scan:
+ ;; Scan for existence of valid expansion ROMS.
+ ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments
+ ;; General ROM: from 0xC8000..0xDFFFF in 2k increments
+ ;; System ROM: only 0xE0000
+ ;;
+ ;; Header:
+ ;; Offset Value
+ ;; 0 0x55
+ ;; 1 0xAA
+ ;; 2 ROM length in 512-byte blocks
+ ;; 3 ROM initialization entry point (FAR CALL)
+
+rom_scan_loop:
+ push ax ;; Save AX
+ mov ds, cx
+ mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
+ cmp [0], #0xAA55 ;; look for signature
+ jne rom_scan_increment
+ call rom_checksum
+ jnz rom_scan_increment
+ mov al, [2] ;; change increment to ROM length in 512-byte blocks
+
+ ;; We want our increment in 512-byte quantities, rounded to
+ ;; the nearest 2k quantity, since we only scan at 2k intervals.
+ test al, #0x03
+ jz block_count_rounded
+ and al, #0xfc ;; needs rounding up
+ add al, #0x04
+block_count_rounded:
+
+ 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, 0x16[bx] ;; 0x16 is the offset of Boot Connection Vector
+ cmp ax, #0x0000
+ je no_bcv
+
+ ;; Option ROM has BCV. Run it now.
+ push cx ;; Push seg
+ push ax ;; Push offset
+
+ ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS.
+ mov bx, #0xf000
+ mov es, bx
+ lea di, pnp_string
+ /* jump to BCV function entry pointer */
+ mov bp, sp ;; Call ROM BCV routine using seg:off on stack
+ db 0xff ;; call_far ss:[bp+0]
+ db 0x5e
+ db 0
+ cli ;; In case expansion ROM BIOS turns IF on
+ add sp, #2 ;; Pop offset value
+ pop cx ;; Pop seg value (restore CX)
+ jmp no_bev
+
+no_bcv:
+ mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of...
+ cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none.
+ je no_bev
+
+ ;; Found a device that thinks it can boot the system. Record its BEV and product name string.
+ mov di, 0x10[bx] ;; Pointer to the product name string or zero if none
+ mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives
+ mov ds, bx
+ mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far
+ cmp bx, #IPL_TABLE_ENTRIES
+ je no_bev ;; Get out if the table is full
+ shl bx, #0x4 ;; Turn count into offset (entries are 16 bytes)
+ mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device
+ mov 6[bx], cx ;; Build a far pointer from the segment...
+ mov 4[bx], ax ;; and the offset
+ cmp di, #0x0000
+ je no_prod_str
+ mov 0xA[bx], cx ;; Build a far pointer from the segment...
+ mov 8[bx], di ;; and the offset
+no_prod_str:
+ shr bx, #0x4 ;; Turn the offset back into a count
+ inc bx ;; We have one more entry now
+ mov IPL_COUNT_OFFSET, bx ;; Remember that.
+
+no_bev:
+ pop di ;; Restore DI
+ pop ax ;; Restore AX
+rom_scan_increment:
+ shl ax, #5 ;; convert 512-bytes blocks to 16-byte increments
+ ;; because the segment selector is shifted left 4 bits.
+ add cx, ax
+ pop ax ;; Restore AX
+ cmp cx, ax
+ jbe rom_scan_loop
+
+ xor ax, ax ;; Restore DS back to 0000:
+ mov ds, ax
+ ret
+
+post_enable_cache:
+ ;; enable cache
+ mov eax, cr0
+ and eax, #0x9fffffff
+ mov cr0, eax
+ jmp post_enable_cache_done
+
+post_init_pic:
+ mov al, #0x11 ; send initialisation commands
+ out 0x20, al
+ out 0xa0, al
+ mov al, #0x08
+ out 0x21, al
+ mov al, #0x70
+ out 0xa1, al
+ mov al, #0x04
+ out 0x21, al
+ mov al, #0x02
+ out 0xa1, al
+ mov al, #0x01
+ out 0x21, al
+ out 0xa1, al
+ mov al, #0xb8
+ out 0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
+#if BX_USE_PS2_MOUSE
+ mov al, #0x8f
+#else
+ mov al, #0x9f
+#endif
+ out 0xa1, AL ;slave pic: unmask IRQ 12, 13, 14
+ ret
+
+;; the following area can be used to write dynamically generated tables
+ .align 16
+bios_table_area_start:
+ dd 0xaafb4442
+ dd bios_table_area_end - bios_table_area_start - 8;
+
+
+;--------
+;- POST -
+;--------
+.org 0xe05b ; POST Entry Point
+post:
+ jmp post_enable_cache ; hack: we have limited space before next .org,
+ ; so take this bit out-of-line
+post_enable_cache_done:
+ xor ax, ax
+
+ ;; first reset the DMA controllers
+ out 0x0d,al
+ out 0xda,al
+
+ ;; then initialize the DMA controllers
+ mov al, #0xC0
+ out 0xD6, al ; cascade mode of channel 4 enabled
+ mov al, #0x00
+ out 0xD4, al ; unmask channel 4
+
+ ;; Examine CMOS shutdown status.
+ mov AL, #0x0f
+ out 0x70, AL
+ in AL, 0x71
+
+ ;; backup status
+ mov bl, al
+
+ ;; Reset CMOS shutdown status.
+ mov AL, #0x0f
+ out 0x70, AL ; select CMOS register Fh
+ mov AL, #0x00
+ out 0x71, AL ; set shutdown action to normal
+
+ ;; Examine CMOS shutdown status.
+ mov al, bl
+
+ ;; 0x00, 0x09, 0x0D+ = normal startup
+ cmp AL, #0x00
+ jz normal_post
+ cmp AL, #0x0d
+ jae normal_post
+ cmp AL, #0x09
+ je normal_post
+
+ ;; 0x05 = eoi + jmp via [0x40:0x67] jump
+ cmp al, #0x05
+ je eoi_jmp_post
+
+ ;; 0x0A = jmp via [0x40:0x67] jump
+ cmp al, #0x0a
+ je jmp_post_0x467
+
+ ;; 0x0B = iret via [0x40:0x67]
+ cmp al, #0x0b
+ je iret_post_0x467
+
+ ;; 0x0C = retf via [0x40:0x67]
+ cmp al, #0x0c
+ je retf_post_0x467
+
+ ;; Examine CMOS shutdown status.
+ ;; 0x01,0x02,0x03,0x04,0x06,0x07,0x08 = Unimplemented shutdown status.
+ push bx
+ call _shutdown_status_panic
+
+#if 0
+ HALT(__LINE__)
+ ;
+ ;#if 0
+ ; 0xb0, 0x20, /* mov al, #0x20 */
+ ; 0xe6, 0x20, /* out 0x20, al ;send EOI to PIC */
+ ;#endif
+ ;
+ pop es
+ pop ds
+ popa
+ iret
+#endif
+
+normal_post:
+ ; case 0: normal startup
+
+ cli
+ mov ax, #0xfffe
+ mov sp, ax
+ xor ax, ax
+ mov ds, ax
+ mov ss, ax
+
+ ;; Save shutdown status
+ mov 0x04b0, bl
+
+ cmp bl, #0xfe
+ jz s3_post
+
+ ;; zero out BIOS data area (40:00..40:ff)
+ mov es, ax
+ mov cx, #0x0080 ;; 128 words
+ mov di, #0x0400
+ cld
+ rep
+ stosw
+
+ call _log_bios_start
+
+ ;; set all interrupts to default handler
+ xor bx, bx ;; offset index
+ mov cx, #0x0100 ;; counter (256 interrupts)
+ mov ax, #dummy_iret_handler
+ mov dx, #0xF000
+
+post_default_ints:
+ mov [bx], ax
+ add bx, #2
+ mov [bx], dx
+ add bx, #2
+ loop post_default_ints
+
+ ;; set vector 0x79 to zero
+ ;; this is used by 'gardian angel' protection system
+ SET_INT_VECTOR(0x79, #0, #0)
+
+ ;; base memory in K 40:13 (word)
+ mov ax, #BASE_MEM_IN_K
+ mov 0x0413, ax
+
+
+ ;; Manufacturing Test 40:12
+ ;; zerod out above
+
+ ;; Warm Boot Flag 0040:0072
+ ;; value of 1234h = skip memory checks
+ ;; zerod out above
+
+
+ ;; Printer Services vector
+ SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
+
+ ;; Bootstrap failure vector
+ SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
+
+ ;; Bootstrap Loader vector
+ SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
+
+ ;; User Timer Tick vector
+ SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
+
+ ;; Memory Size Check vector
+ SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
+
+ ;; Equipment Configuration Check vector
+ SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
+
+ ;; System Services
+ SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
+
+ ;; EBDA setup
+ call ebda_post
+
+ ;; PIT setup
+ SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
+ ;; int 1C already points at dummy_iret_handler (above)
+ mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
+ out 0x43, al
+ mov al, #0x00 ; maximum count of 0000H = 18.2Hz
+ out 0x40, al
+ out 0x40, al
+
+ ;; Keyboard
+ SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
+ SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
+
+ xor ax, ax
+ mov ds, ax
+ mov 0x0417, al /* keyboard shift flags, set 1 */
+ mov 0x0418, al /* keyboard shift flags, set 2 */
+ mov 0x0419, al /* keyboard alt-numpad work area */
+ mov 0x0471, al /* keyboard ctrl-break flag */
+ mov 0x0497, al /* keyboard status flags 4 */
+ mov al, #0x10
+ mov 0x0496, al /* keyboard status flags 3 */
+
+
+ /* keyboard head of buffer pointer */
+ mov bx, #0x001E
+ mov 0x041A, bx
+
+ /* keyboard end of buffer pointer */
+ mov 0x041C, bx
+
+ /* keyboard pointer to start of buffer */
+ mov bx, #0x001E
+ mov 0x0480, bx
+
+ /* keyboard pointer to end of buffer */
+ mov bx, #0x003E
+ mov 0x0482, bx
+
+ /* init the keyboard */
+ call _keyboard_init
+
+ ;; mov CMOS Equipment Byte to BDA Equipment Word
+ mov ax, 0x0410
+ mov al, #0x14
+ out 0x70, al
+ in al, 0x71
+ mov 0x0410, ax
+
+
+ ;; Parallel setup
+ SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
+ xor ax, ax
+ mov ds, ax
+ xor bx, bx
+ mov cl, #0x14 ; timeout value
+ mov dx, #0x378 ; Parallel I/O address, port 1
+ call detect_parport
+ mov dx, #0x278 ; Parallel I/O address, port 2
+ call detect_parport
+ shl bx, #0x0e
+ mov ax, 0x410 ; Equipment word bits 14..15 determing # parallel ports
+ and ax, #0x3fff
+ or ax, bx ; set number of parallel ports
+ mov 0x410, ax
+
+ ;; Serial setup
+ SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
+ SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
+ xor bx, bx
+ mov cl, #0x0a ; timeout value
+ mov dx, #0x03f8 ; Serial I/O address, port 1
+ call detect_serial
+ mov dx, #0x02f8 ; Serial I/O address, port 2
+ call detect_serial
+ mov dx, #0x03e8 ; Serial I/O address, port 3
+ call detect_serial
+ mov dx, #0x02e8 ; Serial I/O address, port 4
+ call detect_serial
+ shl bx, #0x09
+ mov ax, 0x410 ; Equipment word bits 9..11 determing # serial ports
+ and ax, #0xf1ff
+ or ax, bx ; set number of serial port
+ mov 0x410, ax
+
+ ;; CMOS RTC
+ SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
+ SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
+ SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
+ ;; BIOS DATA AREA 0x4CE ???
+ call timer_tick_post
+
+ ;; PS/2 mouse setup
+ SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
+
+ ;; IRQ13 (FPU exception) setup
+ SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
+
+ ;; Video setup
+ SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
+
+ ;; PIC
+ call post_init_pic
+
+ mov cx, #0xc000 ;; init vga bios
+ mov ax, #0xc780
+ call rom_scan
+
+ call _print_bios_banner
+
+#if BX_ROMBIOS32
+ call rombios32_init
+#else
+#if BX_PCIBIOS
+ call pcibios_init_iomem_bases
+ call pcibios_init_irqs
+#endif //BX_PCIBIOS
+#endif
+
+ ;;
+ ;; Floppy setup
+ ;;
+ call floppy_drive_post
+
+ ;;
+ ;; Hard Drive setup
+ ;;
+ call hard_drive_post
+
+#if BX_USE_ATADRV
+
+ ;;
+ ;; ATA/ATAPI driver setup
+ ;;
+ call _ata_init
+ call _ata_detect
+ ;;
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+ ;;
+ ;; eltorito floppy/harddisk emulation from cd
+ ;;
+ call _cdemu_init
+ ;;
+#endif // BX_ELTORITO_BOOT
+
+ call _init_boot_vectors
+
+ mov cx, #0xc800 ;; init option roms
+ mov ax, #0xe000
+ call rom_scan
+
+#if BX_ELTORITO_BOOT
+ call _interactive_bootkey
+#endif // BX_ELTORITO_BOOT
+
+ sti ;; enable interrupts
+ int #0x19
+
+.org 0xe2c3 ; NMI Handler Entry Point
+nmi:
+ ;; FIXME the NMI handler should not panic
+ ;; but iret when called from int75 (fpu exception)
+ call _nmi_handler_msg
+ iret
+
+int75_handler:
+ out 0xf0, al // clear irq13
+ call eoi_both_pics // clear interrupt
+ int 2 // legacy nmi call
+ iret
+
+;-------------------------------------------
+;- INT 13h Fixed Disk Services Entry Point -
+;-------------------------------------------
+.org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
+int13_handler:
+ //JMPL(int13_relocated)
+ jmp int13_relocated
+
+.org 0xe401 ; Fixed Disk Parameter Table
+
+;----------
+;- INT19h -
+;----------
+.org 0xe6f2 ; INT 19h Boot Load Service Entry Point
+int19_handler:
+
+ jmp int19_relocated
+;-------------------------------------------
+;- System BIOS Configuration Data Table
+;-------------------------------------------
+.org BIOS_CONFIG_TABLE
+db 0x08 ; Table size (bytes) -Lo
+db 0x00 ; Table size (bytes) -Hi
+db SYS_MODEL_ID
+db SYS_SUBMODEL_ID
+db BIOS_REVISION
+; Feature byte 1
+; b7: 1=DMA channel 3 used by hard disk
+; b6: 1=2 interrupt controllers present
+; b5: 1=RTC present
+; b4: 1=BIOS calls int 15h/4Fh every key
+; b3: 1=wait for extern event supported (Int 15h/41h)
+; b2: 1=extended BIOS data area used
+; b1: 0=AT or ESDI bus, 1=MicroChannel
+; b0: 1=Dual bus (MicroChannel + ISA)
+db (0 << 7) | \
+ (1 << 6) | \
+ (1 << 5) | \
+ (BX_CALL_INT15_4F << 4) | \
+ (0 << 3) | \
+ (BX_USE_EBDA << 2) | \
+ (0 << 1) | \
+ (0 << 0)
+; Feature byte 2
+; b7: 1=32-bit DMA supported
+; b6: 1=int16h, function 9 supported
+; b5: 1=int15h/C6h (get POS data) supported
+; b4: 1=int15h/C7h (get mem map info) supported
+; b3: 1=int15h/C8h (en/dis CPU) supported
+; b2: 1=non-8042 kb controller
+; b1: 1=data streaming supported
+; b0: reserved
+db (0 << 7) | \
+ (1 << 6) | \
+ (0 << 5) | \
+ (0 << 4) | \
+ (0 << 3) | \
+ (0 << 2) | \
+ (0 << 1) | \
+ (0 << 0)
+; Feature byte 3
+; b7: not used
+; b6: reserved
+; b5: reserved
+; b4: POST supports ROM-to-RAM enable/disable
+; b3: SCSI on system board
+; b2: info panel installed
+; b1: Initial Machine Load (IML) system - BIOS on disk
+; b0: SCSI supported in IML
+db 0x00
+; Feature byte 4
+; b7: IBM private
+; b6: EEPROM present
+; b5-3: ABIOS presence (011 = not supported)
+; b2: private
+; b1: memory split above 16Mb supported
+; b0: POSTEXT directly supported by POST
+db 0x00
+; Feature byte 5 (IBM)
+; b1: enhanced mouse
+; b0: flash EPROM
+db 0x00
+
+
+
+.org 0xe729 ; Baud Rate Generator Table
+
+;----------
+;- INT14h -
+;----------
+.org 0xe739 ; INT 14h Serial Communications Service Entry Point
+int14_handler:
+ push ds
+ pusha
+ xor ax, ax
+ mov ds, ax
+ call _int14_function
+ popa
+ pop ds
+ iret
+
+
+;----------------------------------------
+;- INT 16h Keyboard Service Entry Point -
+;----------------------------------------
+.org 0xe82e
+int16_handler:
+
+ sti
+ push ds
+ pushf
+ pusha
+
+ cmp ah, #0x00
+ je int16_F00
+ cmp ah, #0x10
+ je int16_F00
+
+ mov bx, #0xf000
+ mov ds, bx
+ call _int16_function
+ popa
+ popf
+ pop ds
+ jz int16_zero_set
+
+int16_zero_clear:
+ push bp
+ mov bp, sp
+ //SEG SS
+ and BYTE [bp + 0x06], #0xbf
+ pop bp
+ iret
+
+int16_zero_set:
+ push bp
+ mov bp, sp
+ //SEG SS
+ or BYTE [bp + 0x06], #0x40
+ pop bp
+ iret
+
+int16_F00:
+ mov bx, #0x0040
+ mov ds, bx
+
+int16_wait_for_key:
+ cli
+ mov bx, 0x001a
+ cmp bx, 0x001c
+ jne int16_key_found
+ sti
+ nop
+#if 0
+ /* no key yet, call int 15h, function AX=9002 */
+ 0x50, /* push AX */
+ 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */
+ 0xcd, 0x15, /* int 15h */
+ 0x58, /* pop AX */
+ 0xeb, 0xea, /* jmp WAIT_FOR_KEY */
+#endif
+ jmp int16_wait_for_key
+
+int16_key_found:
+ mov bx, #0xf000
+ mov ds, bx
+ call _int16_function
+ popa
+ popf
+ pop ds
+#if 0
+ /* notify int16 complete w/ int 15h, function AX=9102 */
+ 0x50, /* push AX */
+ 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */
+ 0xcd, 0x15, /* int 15h */
+ 0x58, /* pop AX */
+#endif
+ iret
+
+
+
+;-------------------------------------------------
+;- INT09h : Keyboard Hardware Service Entry Point -
+;-------------------------------------------------
+.org 0xe987
+int09_handler:
+ cli
+ push ax
+
+ mov al, #0xAD ;;disable keyboard
+ out #0x64, al
+
+ mov al, #0x0B
+ out #0x20, al
+ in al, #0x20
+ and al, #0x02
+ jz int09_finish
+
+ in al, #0x60 ;;read key from keyboard controller
+ sti
+ push ds
+ pusha
+#ifdef BX_CALL_INT15_4F
+ mov ah, #0x4f ;; allow for keyboard intercept
+ stc
+ int #0x15
+ jnc int09_done
+#endif
+
+ ;; check for extended key
+ cmp al, #0xe0
+ jne int09_check_pause
+ xor ax, ax
+ mov ds, ax
+ mov al, BYTE [0x496] ;; mf2_state |= 0x02
+ or al, #0x02
+ mov BYTE [0x496], al
+ jmp int09_done
+
+int09_check_pause: ;; check for pause key
+ cmp al, #0xe1
+ jne int09_process_key
+ xor ax, ax
+ mov ds, ax
+ mov al, BYTE [0x496] ;; mf2_state |= 0x01
+ or al, #0x01
+ mov BYTE [0x496], al
+ jmp int09_done
+
+int09_process_key:
+ mov bx, #0xf000
+ mov ds, bx
+ call _int09_function
+
+int09_done:
+ popa
+ pop ds
+ cli
+ call eoi_master_pic
+
+int09_finish:
+ mov al, #0xAE ;;enable keyboard
+ out #0x64, al
+ pop ax
+ iret
+
+
+;----------------------------------------
+;- INT 13h Diskette Service Entry Point -
+;----------------------------------------
+.org 0xec59
+int13_diskette:
+ jmp int13_noeltorito
+
+;---------------------------------------------
+;- INT 0Eh Diskette Hardware ISR Entry Point -
+;---------------------------------------------
+.org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
+int0e_handler:
+ push ax
+ push dx
+ mov dx, #0x03f4
+ in al, dx
+ and al, #0xc0
+ cmp al, #0xc0
+ je int0e_normal
+ mov dx, #0x03f5
+ mov al, #0x08 ; sense interrupt status
+ out dx, al
+int0e_loop1:
+ mov dx, #0x03f4
+ in al, dx
+ and al, #0xc0
+ cmp al, #0xc0
+ jne int0e_loop1
+int0e_loop2:
+ mov dx, #0x03f5
+ in al, dx
+ mov dx, #0x03f4
+ in al, dx
+ and al, #0xc0
+ cmp al, #0xc0
+ je int0e_loop2
+int0e_normal:
+ push ds
+ xor ax, ax ;; segment 0000
+ mov ds, ax
+ call eoi_master_pic
+ mov al, 0x043e
+ or al, #0x80 ;; diskette interrupt has occurred
+ mov 0x043e, al
+ pop ds
+ pop dx
+ pop ax
+ iret
+
+
+.org 0xefc7 ; Diskette Controller Parameter Table
+diskette_param_table:
+;; Since no provisions are made for multiple drive types, most
+;; values in this table are ignored. I set parameters for 1.44M
+;; floppy here
+db 0xAF
+db 0x02 ;; head load time 0000001, DMA used
+db 0x25
+db 0x02
+db 18
+db 0x1B
+db 0xFF
+db 0x6C
+db 0xF6
+db 0x0F
+db 0x08
+
+
+;----------------------------------------
+;- INT17h : Printer Service Entry Point -
+;----------------------------------------
+.org 0xefd2
+int17_handler:
+ push ds
+ pusha
+ xor ax, ax
+ mov ds, ax
+ call _int17_function
+ popa
+ pop ds
+ iret
+
+diskette_param_table2:
+;; New diskette parameter table adding 3 parameters from IBM
+;; Since no provisions are made for multiple drive types, most
+;; values in this table are ignored. I set parameters for 1.44M
+;; floppy here
+db 0xAF
+db 0x02 ;; head load time 0000001, DMA used
+db 0x25
+db 0x02
+db 18
+db 0x1B
+db 0xFF
+db 0x6C
+db 0xF6
+db 0x0F
+db 0x08
+db 79 ;; maximum track
+db 0 ;; data transfer rate
+db 4 ;; drive type in cmos
+
+.org 0xf045 ; INT 10 Functions 0-Fh Entry Point
+ HALT(__LINE__)
+ iret
+
+;----------
+;- INT10h -
+;----------
+.org 0xf065 ; INT 10h Video Support Service Entry Point
+int10_handler:
+ ;; dont do anything, since the VGA BIOS handles int10h requests
+ iret
+
+.org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
+
+;----------
+;- INT12h -
+;----------
+.org 0xf841 ; INT 12h Memory Size Service Entry Point
+; ??? different for Pentium (machine check)?
+int12_handler:
+ push ds
+ mov ax, #0x0040
+ mov ds, ax
+ mov ax, 0x0013
+ pop ds
+ iret
+
+;----------
+;- INT11h -
+;----------
+.org 0xf84d ; INT 11h Equipment List Service Entry Point
+int11_handler:
+ push ds
+ mov ax, #0x0040
+ mov ds, ax
+ mov ax, 0x0010
+ pop ds
+ iret
+
+;----------
+;- INT15h -
+;----------
+.org 0xf859 ; INT 15h System Services Entry Point
+int15_handler:
+ pushf
+#if BX_APM
+ cmp ah, #0x53
+ je apm_call
+#endif
+ push ds
+ push es
+ cmp ah, #0x86
+ je int15_handler32
+ cmp ah, #0xE8
+ je int15_handler32
+ pusha
+#if BX_USE_PS2_MOUSE
+ cmp ah, #0xC2
+ je int15_handler_mouse
+#endif
+ call _int15_function
+int15_handler_mouse_ret:
+ popa
+int15_handler32_ret:
+ pop es
+ pop ds
+ popf
+ jmp iret_modify_cf
+#if BX_APM
+apm_call:
+ jmp _apmreal_entry
+#endif
+
+#if BX_USE_PS2_MOUSE
+int15_handler_mouse:
+ call _int15_function_mouse
+ jmp int15_handler_mouse_ret
+#endif
+
+int15_handler32:
+ pushad
+ call _int15_function32
+ popad
+ jmp int15_handler32_ret
+
+;; Protected mode IDT descriptor
+;;
+;; I just make the limit 0, so the machine will shutdown
+;; if an exception occurs during protected mode memory
+;; transfers.
+;;
+;; Set base to f0000 to correspond to beginning of BIOS,
+;; in case I actually define an IDT later
+;; Set limit to 0
+
+pmode_IDT_info:
+dw 0x0000 ;; limit 15:00
+dw 0x0000 ;; base 15:00
+db 0x0f ;; base 23:16
+
+;; Real mode IDT descriptor
+;;
+;; Set to typical real-mode values.
+;; base = 000000
+;; limit = 03ff
+
+rmode_IDT_info:
+dw 0x03ff ;; limit 15:00
+dw 0x0000 ;; base 15:00
+db 0x00 ;; base 23:16
+
+
+;----------
+;- INT1Ah -
+;----------
+.org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
+int1a_handler:
+#if BX_PCIBIOS
+ cmp ah, #0xb1
+ jne int1a_normal
+ call pcibios_real
+ jc pcibios_error
+ retf 2
+pcibios_error:
+ mov bl, ah
+ mov ah, #0xb1
+ push ds
+ pusha
+ mov ax, ss ; set readable descriptor to ds, for calling pcibios
+ mov ds, ax ; on 16bit protected mode.
+ jmp int1a_callfunction
+int1a_normal:
+#endif
+ push ds
+ pusha
+ xor ax, ax
+ mov ds, ax
+int1a_callfunction:
+ call _int1a_function
+ popa
+ pop ds
+ iret
+
+;;
+;; int70h: IRQ8 - CMOS RTC
+;;
+int70_handler:
+ push ds
+ pushad
+ xor ax, ax
+ mov ds, ax
+ call _int70_function
+ popad
+ pop ds
+ iret
+
+;---------
+;- INT08 -
+;---------
+.org 0xfea5 ; INT 08h System Timer ISR Entry Point
+int08_handler:
+ sti
+ push eax
+ push ds
+ xor ax, ax
+ mov ds, ax
+
+ ;; time to turn off drive(s)?
+ mov al,0x0440
+ or al,al
+ jz int08_floppy_off
+ dec al
+ mov 0x0440,al
+ jnz int08_floppy_off
+ ;; turn motor(s) off
+ push dx
+ mov dx,#0x03f2
+ in al,dx
+ and al,#0xcf
+ out dx,al
+ pop dx
+int08_floppy_off:
+
+ mov eax, 0x046c ;; get ticks dword
+ inc eax
+
+ ;; compare eax to one days worth of timer ticks at 18.2 hz
+ cmp eax, #0x001800B0
+ jb int08_store_ticks
+ ;; there has been a midnight rollover at this point
+ xor eax, eax ;; zero out counter
+ inc BYTE 0x0470 ;; increment rollover flag
+
+int08_store_ticks:
+ mov 0x046c, eax ;; store new ticks dword
+ ;; chain to user timer tick INT #0x1c
+ //pushf
+ //;; call_ep [ds:loc]
+ //CALL_EP( 0x1c << 2 )
+ int #0x1c
+ cli
+ call eoi_master_pic
+ pop ds
+ pop eax
+ iret
+
+.org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
+
+
+.org 0xff00
+.ascii BIOS_COPYRIGHT_STRING
+
+;------------------------------------------------
+;- IRET Instruction for Dummy Interrupt Handler -
+;------------------------------------------------
+.org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
+dummy_iret_handler:
+ iret
+
+.org 0xff54 ; INT 05h Print Screen Service Entry Point
+ HALT(__LINE__)
+ iret
+
+.org 0xfff0 ; Power-up Entry Point
+ jmp 0xf000:post
+
+.org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
+.ascii BIOS_BUILD_DATE
+
+.org 0xfffe ; System Model ID
+db SYS_MODEL_ID
+db 0x00 ; filler
+
+.org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+ASM_END
+/*
+ * This font comes from the fntcol16.zip package (c) by Joseph Gil
+ * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+ * This font is public domain
+ */
+static Bit8u vgafont8[128*8]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+};
+
+ASM_START
+.org 0xcc00
+bios_table_area_end:
+// bcc-generated data will be placed here
+ASM_END
diff --git a/kvm/bios/rombios.h b/kvm/bios/rombios.h
new file mode 100644
index 000000000..aadee7a42
--- /dev/null
+++ b/kvm/bios/rombios.h
@@ -0,0 +1,70 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rombios.h,v 1.4 2007/02/20 09:36:55 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2006 Volker Ruppert
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+/* define it to include QEMU specific code */
+#define BX_QEMU
+
+#ifndef LEGACY
+# define BX_ROMBIOS32 1
+#else
+# define BX_ROMBIOS32 0
+#endif
+#define DEBUG_ROMBIOS 0
+
+#define PANIC_PORT 0x400
+#define PANIC_PORT2 0x401
+#define INFO_PORT 0x402
+#define DEBUG_PORT 0x403
+
+#define BIOS_PRINTF_HALT 1
+#define BIOS_PRINTF_SCREEN 2
+#define BIOS_PRINTF_INFO 4
+#define BIOS_PRINTF_DEBUG 8
+#define BIOS_PRINTF_ALL (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
+#define BIOS_PRINTF_DEBHALT (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
+
+#define printf(format, p...) bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
+
+// Defines the output macros.
+// BX_DEBUG goes to INFO port until we can easily choose debug info on a
+// per-device basis. Debug info are sent only in debug mode
+#if DEBUG_ROMBIOS
+# define BX_DEBUG(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
+#else
+# define BX_DEBUG(format, p...)
+#endif
+#define BX_INFO(format, p...) bios_printf(BIOS_PRINTF_INFO, format, ##p)
+#define BX_PANIC(format, p...) bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
+
+#define ACPI_DATA_SIZE 0x00010000L
+#define PM_IO_BASE 0xb000
+#define SMB_IO_BASE 0xb100
+#define SMP_MSR_ADDR 0x0510
+
+#define MAX_CPUS 16
+
+ // Define the application NAME
+#if defined(BX_QEMU)
+# define BX_APPNAME "QEMU"
+#elif defined(PLEX86)
+# define BX_APPNAME "Plex86"
+#else
+# define BX_APPNAME "Bochs"
+#endif
diff --git a/kvm/bios/rombios32.c b/kvm/bios/rombios32.c
new file mode 100755
index 000000000..cceaedef0
--- /dev/null
+++ b/kvm/bios/rombios32.c
@@ -0,0 +1,2260 @@
+/////////////////////////////////////////////////////////////////////////
+// $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
+
+/* IRQs 5,9,10,11 */
+#define PCI_ISA_IRQ_MASK 0x0e20U
+
+#define APIC_ENABLED 0x0100
+
+#define AP_BOOT_ADDR 0x9f000
+
+#define MPTABLE_MAX_SIZE 0x00002000
+#define SMI_CMD_IO_ADDR 0xb2
+
+#define BIOS_TMP_STORAGE 0x00030000 /* 64 KB used to copy the BIOS to shadow RAM */
+
+#define MSR_MTRRcap 0x000000fe
+#define MSR_MTRRfix64K_00000 0x00000250
+#define MSR_MTRRfix16K_80000 0x00000258
+#define MSR_MTRRfix16K_A0000 0x00000259
+#define MSR_MTRRfix4K_C0000 0x00000268
+#define MSR_MTRRfix4K_C8000 0x00000269
+#define MSR_MTRRfix4K_D0000 0x0000026a
+#define MSR_MTRRfix4K_D8000 0x0000026b
+#define MSR_MTRRfix4K_E0000 0x0000026c
+#define MSR_MTRRfix4K_E8000 0x0000026d
+#define MSR_MTRRfix4K_F0000 0x0000026e
+#define MSR_MTRRfix4K_F8000 0x0000026f
+#define MSR_MTRRdefType 0x000002ff
+
+#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg))
+#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1)
+
+static inline void outl(int addr, int val)
+{
+ asm volatile ("outl %1, %w0" : : "d" (addr), "a" (val));
+}
+
+static inline void outw(int addr, int val)
+{
+ asm volatile ("outw %w1, %w0" : : "d" (addr), "a" (val));
+}
+
+static inline void outb(int addr, int val)
+{
+ asm volatile ("outb %b1, %w0" : : "d" (addr), "a" (val));
+}
+
+static inline uint32_t inl(int addr)
+{
+ uint32_t val;
+ asm volatile ("inl %w1, %0" : "=a" (val) : "d" (addr));
+ return val;
+}
+
+static inline uint16_t inw(int addr)
+{
+ uint16_t val;
+ asm volatile ("inw %w1, %w0" : "=a" (val) : "d" (addr));
+ return val;
+}
+
+static inline uint8_t inb(int addr)
+{
+ uint8_t val;
+ asm volatile ("inb %w1, %b0" : "=a" (val) : "d" (addr));
+ return val;
+}
+
+static inline void writel(void *addr, uint32_t val)
+{
+ *(volatile uint32_t *)addr = val;
+}
+
+static inline void writew(void *addr, uint16_t val)
+{
+ *(volatile uint16_t *)addr = val;
+}
+
+static inline void writeb(void *addr, uint8_t val)
+{
+ *(volatile uint8_t *)addr = val;
+}
+
+static inline uint32_t readl(const void *addr)
+{
+ return *(volatile const uint32_t *)addr;
+}
+
+static inline uint16_t readw(const void *addr)
+{
+ return *(volatile const uint16_t *)addr;
+}
+
+static inline uint8_t readb(const void *addr)
+{
+ return *(volatile const uint8_t *)addr;
+}
+
+static inline void putc(int c)
+{
+ outb(INFO_PORT, c);
+}
+
+static uint64_t rdmsr(unsigned index)
+{
+ unsigned long long ret;
+
+ asm ("rdmsr" : "=A"(ret) : "c"(index));
+ return ret;
+}
+
+static void wrmsr(unsigned index, uint64_t val)
+{
+ asm volatile ("wrmsr" : : "c"(index), "A"(val));
+}
+
+static inline int isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+void *memset(void *d1, int val, size_t len)
+{
+ uint8_t *d = d1;
+
+ while (len--) {
+ *d++ = val;
+ }
+ return d1;
+}
+
+void *memcpy(void *d1, const void *s1, size_t len)
+{
+ uint8_t *d = d1;
+ const uint8_t *s = s1;
+
+ while (len--) {
+ *d++ = *s++;
+ }
+ return d1;
+}
+
+void *memmove(void *d1, const void *s1, size_t len)
+{
+ uint8_t *d = d1;
+ const uint8_t *s = s1;
+
+ if (d <= s) {
+ while (len--) {
+ *d++ = *s++;
+ }
+ } else {
+ d += len;
+ s += len;
+ while (len--) {
+ *--d = *--s;
+ }
+ }
+ return d1;
+}
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+ const int8_t *p1 = s1;
+ const int8_t *p2 = s2;
+
+ while (len--) {
+ int r = *p1++ - *p2++;
+ if(r)
+ return r;
+ }
+
+ return 0;
+}
+
+size_t strlen(const char *s)
+{
+ const char *s1;
+ for(s1 = s; *s1 != '\0'; s1++);
+ return s1 - s;
+}
+
+/* from BSD ppp sources */
+int vsnprintf(char *buf, int buflen, const char *fmt, va_list args)
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg;
+ unsigned long val = 0;
+ const char *f;
+ char *str, *buf0;
+ char num[32];
+ static const char hexchars[] = "0123456789abcdef";
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = prec = 0;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ /* modifiers */
+ switch(c) {
+ case 'l':
+ c = *++fmt;
+ break;
+ default:
+ break;
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec > 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
+
+int snprintf(char * buf, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i=vsnprintf(buf,size,fmt,args);
+ va_end(args);
+ return i;
+}
+
+void bios_printf(int flags, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ const char *s;
+
+ if ((flags & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT)
+ outb(PANIC_PORT2, 0x00);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ s = buf;
+ while (*s)
+ putc(*s++);
+ va_end(ap);
+}
+
+void delay_ms(int n)
+{
+ int i, j;
+ for(i = 0; i < n; i++) {
+#ifdef BX_QEMU
+ /* approximative ! */
+ for(j = 0; j < 1000000; j++);
+#else
+ {
+ int r1, r2;
+ j = 66;
+ r1 = inb(0x61) & 0x10;
+ do {
+ r2 = inb(0x61) & 0x10;
+ if (r1 != r2) {
+ j--;
+ r1 = r2;
+ }
+ } while (j > 0);
+ }
+#endif
+ }
+}
+
+uint16_t smp_cpus;
+uint32_t cpuid_signature;
+uint32_t cpuid_features;
+uint32_t cpuid_ext_features;
+unsigned long ram_size;
+uint64_t ram_end;
+uint8_t bios_uuid[16];
+#ifdef BX_USE_EBDA_TABLES
+unsigned long ebda_cur_addr;
+#endif
+int acpi_enabled;
+uint32_t pm_io_base, smb_io_base;
+int pm_sci_int;
+unsigned long bios_table_cur_addr;
+unsigned long bios_table_end_addr;
+
+void init_smp_msrs(void)
+{
+ *(uint32_t *)SMP_MSR_ADDR = 0;
+}
+
+void wrmsr_smp(uint32_t index, uint64_t val)
+{
+ static struct { uint32_t ecx, eax, edx; } *p = (void *)SMP_MSR_ADDR;
+
+ wrmsr(index, val);
+ p->ecx = index;
+ p->eax = val;
+ p->edx = val >> 32;
+ ++p;
+ p->ecx = 0;
+}
+
+#ifdef BX_QEMU
+#define QEMU_CFG_CTL_PORT 0x510
+#define QEMU_CFG_DATA_PORT 0x511
+#define QEMU_CFG_SIGNATURE 0x00
+#define QEMU_CFG_ID 0x01
+#define QEMU_CFG_UUID 0x02
+
+int qemu_cfg_port;
+
+void qemu_cfg_select(int f)
+{
+ outw(QEMU_CFG_CTL_PORT, f);
+}
+
+int qemu_cfg_port_probe()
+{
+ char *sig = "QEMU";
+ int i;
+
+ qemu_cfg_select(QEMU_CFG_SIGNATURE);
+
+ for (i = 0; i < 4; i++)
+ if (inb(QEMU_CFG_DATA_PORT) != sig[i])
+ return 0;
+
+ return 1;
+}
+
+void qemu_cfg_read(uint8_t *buf, int len)
+{
+ while (len--)
+ *(buf++) = inb(QEMU_CFG_DATA_PORT);
+}
+#endif
+
+void uuid_probe(void)
+{
+#ifdef BX_QEMU
+ if(qemu_cfg_port) {
+ qemu_cfg_select(QEMU_CFG_UUID);
+ qemu_cfg_read(bios_uuid, 16);
+ return;
+ }
+#endif
+ memset(bios_uuid, 0, 16);
+}
+
+void cpu_probe(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ cpuid(1, eax, ebx, ecx, edx);
+ cpuid_signature = eax;
+ cpuid_features = edx;
+ cpuid_ext_features = ecx;
+}
+
+static int cmos_readb(int addr)
+{
+ outb(0x70, addr);
+ return inb(0x71);
+}
+
+void setup_mtrr(void)
+{
+ int i, vcnt, fix, wc;
+ uint32_t mtrr_cap;
+ union {
+ uint8_t valb[8];
+ uint64_t val;
+ } u;
+
+ mtrr_cap = rdmsr(MSR_MTRRcap);
+ vcnt = mtrr_cap & 0xff;
+ fix = mtrr_cap & 0x100;
+ wc = mtrr_cap & 0x400;
+ if (!vcnt || !fix)
+ return;
+ u.val = 0;
+ for (i = 0; i < 8; ++i)
+ if (ram_size >= 65536 * (i + 1))
+ u.valb[i] = 6;
+ wrmsr_smp(MSR_MTRRfix64K_00000, u.val);
+ u.val = 0;
+ for (i = 0; i < 8; ++i)
+ if (ram_size >= 65536 * 8 + 16384 * (i + 1))
+ u.valb[i] = 6;
+ wrmsr_smp(MSR_MTRRfix16K_80000, u.val);
+ wrmsr_smp(MSR_MTRRfix16K_A0000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_C0000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_C8000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_D0000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_D8000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_E0000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_E8000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_F0000, 0);
+ wrmsr_smp(MSR_MTRRfix4K_F8000, 0);
+ /* Mark 3.5-4GB as UC, anything not specified defaults to WB */
+ wrmsr_smp(MTRRphysBase_MSR(0), 0xe0000000ull | 0);
+ wrmsr_smp(MTRRphysMask_MSR(0), ~(0x20000000ull - 1) | 0x800);
+ wrmsr_smp(MSR_MTRRdefType, 0xc06);
+}
+
+void ram_probe(void)
+{
+ if (cmos_readb(0x34) | cmos_readb(0x35))
+ ram_size = (cmos_readb(0x34) | (cmos_readb(0x35) << 8)) * 65536 +
+ 16 * 1024 * 1024;
+ else
+ ram_size = (cmos_readb(0x17) | (cmos_readb(0x18) << 8)) * 1024;
+
+ if (cmos_readb(0x5b) | cmos_readb(0x5c) | cmos_readb(0x5d))
+ ram_end = (((uint64_t)cmos_readb(0x5b) << 16) |
+ ((uint64_t)cmos_readb(0x5c) << 24) |
+ ((uint64_t)cmos_readb(0x5d) << 32)) + (1ull << 32);
+ else
+ ram_end = ram_size;
+
+ BX_INFO("end of ram=%ldMB\n", ram_end >> 20);
+
+ BX_INFO("ram_size=0x%08lx\n", ram_size);
+#ifdef BX_USE_EBDA_TABLES
+ ebda_cur_addr = ((*(uint16_t *)(0x40e)) << 4) + 0x380;
+ BX_INFO("ebda_cur_addr: 0x%08lx\n", ebda_cur_addr);
+#endif
+ setup_mtrr();
+}
+
+/****************************************************/
+/* SMP probe */
+
+extern uint8_t smp_ap_boot_code_start;
+extern uint8_t smp_ap_boot_code_end;
+
+/* find the number of CPUs by launching a SIPI to them */
+void smp_probe(void)
+{
+ uint32_t val, sipi_vector;
+
+ writew(&smp_cpus, 1);
+ if (cpuid_features & CPUID_APIC) {
+
+ /* enable local APIC */
+ val = readl(APIC_BASE + APIC_SVR);
+ val |= APIC_ENABLED;
+ writel(APIC_BASE + APIC_SVR, val);
+
+ /* copy AP boot code */
+ memcpy((void *)AP_BOOT_ADDR, &smp_ap_boot_code_start,
+ &smp_ap_boot_code_end - &smp_ap_boot_code_start);
+
+ /* broadcast SIPI */
+ writel(APIC_BASE + APIC_ICR_LOW, 0x000C4500);
+ sipi_vector = AP_BOOT_ADDR >> 12;
+ writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector);
+ asm volatile(
+ "xor %%eax, %%eax \n\t"
+ "xor %%edx, %%edx \n\t"
+ "mov $0x10, %%ecx \n\t"
+ "wrmsr"
+ : : : "eax", "ecx", "edx");
+
+#ifndef BX_QEMU
+ delay_ms(10);
+#else
+ while (cmos_readb(0x5f) + 1 != smp_cpus)
+ ;
+#endif
+ }
+ BX_INFO("Found %d cpu(s)\n", readw(&smp_cpus));
+}
+
+/****************************************************/
+/* PCI init */
+
+#define PCI_ADDRESS_SPACE_MEM 0x00
+#define PCI_ADDRESS_SPACE_IO 0x01
+#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+
+#define PCI_DEVICES_MAX 64
+
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_82441 0x1237
+#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
+#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+
+#define PCI_VENDOR_ID_IBM 0x1014
+#define PCI_VENDOR_ID_APPLE 0x106b
+
+typedef struct PCIDevice {
+ int bus;
+ int devfn;
+} PCIDevice;
+
+static uint32_t pci_bios_io_addr;
+static uint32_t pci_bios_mem_addr;
+static uint32_t pci_bios_bigmem_addr;
+/* host irqs corresponding to PCI irqs A-D */
+static uint8_t pci_irqs[4] = { 10, 10, 11, 11 };
+static PCIDevice i440_pcidev;
+
+static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val)
+{
+ outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc));
+ outl(0xcfc, val);
+}
+
+static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val)
+{
+ outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc));
+ outw(0xcfc + (addr & 2), val);
+}
+
+static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val)
+{
+ outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc));
+ outb(0xcfc + (addr & 3), val);
+}
+
+static uint32_t pci_config_readl(PCIDevice *d, uint32_t addr)
+{
+ outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc));
+ return inl(0xcfc);
+}
+
+static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr)
+{
+ outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc));
+ return inw(0xcfc + (addr & 2));
+}
+
+static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr)
+{
+ outl(0xcf8, 0x80000000 | (d->bus << 16) | (d->devfn << 8) | (addr & 0xfc));
+ return inb(0xcfc + (addr & 3));
+}
+
+static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr)
+{
+ uint16_t cmd;
+ uint32_t ofs, old_addr;
+
+ if ( region_num == PCI_ROM_SLOT ) {
+ ofs = 0x30;
+ }else{
+ ofs = 0x10 + region_num * 4;
+ }
+
+ old_addr = pci_config_readl(d, ofs);
+
+ pci_config_writel(d, ofs, addr);
+ BX_INFO("region %d: 0x%08x\n", region_num, addr);
+
+ /* enable memory mappings */
+ cmd = pci_config_readw(d, PCI_COMMAND);
+ if ( region_num == PCI_ROM_SLOT )
+ cmd |= 2;
+ else if (old_addr & PCI_ADDRESS_SPACE_IO)
+ cmd |= 1;
+ else
+ cmd |= 2;
+ pci_config_writew(d, PCI_COMMAND, cmd);
+}
+
+/* return the global irq number corresponding to a given device irq
+ pin. We could also use the bus number to have a more precise
+ mapping. */
+static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot_addend;
+ slot_addend = (pci_dev->devfn >> 3) - 1;
+ return (irq_num + slot_addend) & 3;
+}
+
+static void find_bios_table_area(void)
+{
+ unsigned long addr;
+ for(addr = 0xf0000; addr < 0x100000; addr += 16) {
+ if (*(uint32_t *)addr == 0xaafb4442) {
+ bios_table_cur_addr = addr + 8;
+ bios_table_end_addr = bios_table_cur_addr + *(uint32_t *)(addr + 4);
+ BX_INFO("bios_table_addr: 0x%08lx end=0x%08lx\n",
+ bios_table_cur_addr, bios_table_end_addr);
+ return;
+ }
+ }
+ return;
+}
+
+static void bios_shadow_init(PCIDevice *d)
+{
+ int v;
+
+ if (bios_table_cur_addr == 0)
+ return;
+
+ /* remap the BIOS to shadow RAM an keep it read/write while we
+ are writing tables */
+ v = pci_config_readb(d, 0x59);
+ v &= 0xcf;
+ pci_config_writeb(d, 0x59, v);
+ memcpy((void *)BIOS_TMP_STORAGE, (void *)0x000f0000, 0x10000);
+ v |= 0x30;
+ pci_config_writeb(d, 0x59, v);
+ memcpy((void *)0x000f0000, (void *)BIOS_TMP_STORAGE, 0x10000);
+
+ i440_pcidev = *d;
+}
+
+static void bios_lock_shadow_ram(void)
+{
+ PCIDevice *d = &i440_pcidev;
+ int v;
+
+ wbinvd();
+ v = pci_config_readb(d, 0x59);
+ v = (v & 0x0f) | (0x10);
+ pci_config_writeb(d, 0x59, v);
+}
+
+static void pci_bios_init_bridges(PCIDevice *d)
+{
+ uint16_t vendor_id, device_id;
+
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ (device_id == PCI_DEVICE_ID_INTEL_82371SB_0 ||
+ device_id == PCI_DEVICE_ID_INTEL_82371AB_0)) {
+ int i, irq;
+ uint8_t elcr[2];
+
+ /* PIIX3/PIIX4 PCI to ISA bridge */
+
+ elcr[0] = 0x00;
+ elcr[1] = 0x00;
+ for(i = 0; i < 4; i++) {
+ irq = pci_irqs[i];
+ /* set to trigger level */
+ elcr[irq >> 3] |= (1 << (irq & 7));
+ /* activate irq remapping in PIIX */
+ pci_config_writeb(d, 0x60 + i, irq);
+ }
+ outb(0x4d0, elcr[0]);
+ outb(0x4d1, elcr[1]);
+ BX_INFO("PIIX3/PIIX4 init: elcr=%02x %02x\n",
+ elcr[0], elcr[1]);
+ } else if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82441) {
+ /* i440 PCI bridge */
+ bios_shadow_init(d);
+ }
+}
+
+extern uint8_t smm_relocation_start, smm_relocation_end;
+extern uint8_t smm_code_start, smm_code_end;
+
+#ifdef BX_USE_SMM
+static void smm_init(PCIDevice *d)
+{
+ uint32_t value;
+
+ /* check if SMM init is already done */
+ value = pci_config_readl(d, 0x58);
+ if ((value & (1 << 25)) == 0) {
+
+ /* enable the SMM memory window */
+ pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x48);
+
+ /* save original memory content */
+ memcpy((void *)0xa8000, (void *)0x38000, 0x8000);
+
+ /* copy the SMM relocation code */
+ memcpy((void *)0x38000, &smm_relocation_start,
+ &smm_relocation_end - &smm_relocation_start);
+
+ /* enable SMI generation when writing to the APMC register */
+ pci_config_writel(d, 0x58, value | (1 << 25));
+
+ /* init APM status port */
+ outb(0xb3, 0x01);
+
+ /* raise an SMI interrupt */
+ outb(0xb2, 0x00);
+
+ /* wait until SMM code executed */
+ while (inb(0xb3) != 0x00);
+
+ /* restore original memory content */
+ memcpy((void *)0x38000, (void *)0xa8000, 0x8000);
+
+ /* copy the SMM code */
+ memcpy((void *)0xa8000, &smm_code_start,
+ &smm_code_end - &smm_code_start);
+ wbinvd();
+
+ /* close the SMM memory window and enable normal SMM */
+ pci_config_writeb(&i440_pcidev, 0x72, 0x02 | 0x08);
+ }
+}
+#endif
+
+static void piix4_pm_enable(PCIDevice *d)
+{
+ /* PIIX4 Power Management device (for ACPI) */
+ pci_config_writel(d, 0x40, PM_IO_BASE | 1);
+ pci_config_writeb(d, 0x80, 0x01); /* enable PM io space */
+ pci_config_writel(d, 0x90, SMB_IO_BASE | 1);
+ pci_config_writeb(d, 0xd2, 0x09); /* enable SMBus io space */
+#ifdef BX_USE_SMM
+ smm_init(d);
+#endif
+}
+
+static void pci_bios_init_device(PCIDevice *d)
+{
+ int class;
+ uint32_t *paddr;
+ int i, pin, pic_irq, vendor_id, device_id;
+
+ class = pci_config_readw(d, PCI_CLASS_DEVICE);
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+ BX_INFO("PCI: bus=%d devfn=0x%02x: vendor_id=0x%04x device_id=0x%04x class=0x%04x\n",
+ d->bus, d->devfn, vendor_id, device_id, class);
+ switch(class) {
+ case 0x0101: /* Mass storage controller - IDE interface */
+ if (vendor_id == PCI_VENDOR_ID_INTEL &&
+ (device_id == PCI_DEVICE_ID_INTEL_82371SB_1 ||
+ device_id == PCI_DEVICE_ID_INTEL_82371AB)) {
+ /* PIIX3/PIIX4 IDE */
+ pci_config_writew(d, 0x40, 0x8000); // enable IDE0
+ pci_config_writew(d, 0x42, 0x8000); // enable IDE1
+ goto default_map;
+ } else {
+ /* IDE: we map it as in ISA mode */
+ pci_set_io_region_addr(d, 0, 0x1f0);
+ pci_set_io_region_addr(d, 1, 0x3f4);
+ pci_set_io_region_addr(d, 2, 0x170);
+ pci_set_io_region_addr(d, 3, 0x374);
+ }
+ break;
+ case 0x0300: /* Display controller - VGA compatible controller */
+ if (vendor_id != 0x1234)
+ goto default_map;
+ /* VGA: map frame buffer to default Bochs VBE address */
+ pci_set_io_region_addr(d, 0, 0xE0000000);
+ break;
+ case 0x0800: /* Generic system peripheral - PIC */
+ if (vendor_id == PCI_VENDOR_ID_IBM) {
+ /* IBM */
+ if (device_id == 0x0046 || device_id == 0xFFFF) {
+ /* MPIC & MPIC2 */
+ pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000);
+ }
+ }
+ break;
+ case 0xff00:
+ if (vendor_id == PCI_VENDOR_ID_APPLE &&
+ (device_id == 0x0017 || device_id == 0x0022)) {
+ /* macio bridge */
+ pci_set_io_region_addr(d, 0, 0x80800000);
+ }
+ break;
+ default:
+ default_map:
+ /* default memory mappings */
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ int ofs;
+ uint32_t val, size ;
+
+ if (i == PCI_ROM_SLOT)
+ ofs = 0x30;
+ else
+ ofs = 0x10 + i * 4;
+ pci_config_writel(d, ofs, 0xffffffff);
+ val = pci_config_readl(d, ofs);
+ if (val != 0) {
+ size = (~(val & ~0xf)) + 1;
+ if (val & PCI_ADDRESS_SPACE_IO)
+ paddr = &pci_bios_io_addr;
+ else if (size >= 0x04000000)
+ paddr = &pci_bios_bigmem_addr;
+ else
+ paddr = &pci_bios_mem_addr;
+ *paddr = (*paddr + size - 1) & ~(size - 1);
+ pci_set_io_region_addr(d, i, *paddr);
+ *paddr += size;
+ }
+ }
+ break;
+ }
+
+ /* map the interrupt */
+ pin = pci_config_readb(d, PCI_INTERRUPT_PIN);
+ if (pin != 0) {
+ pin = pci_slot_get_pirq(d, pin - 1);
+ pic_irq = pci_irqs[pin];
+ pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq);
+ }
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82371AB_3) {
+ /* PIIX4 Power Management device (for ACPI) */
+
+ // acpi sci is hardwired to 9
+ pci_config_writeb(d, PCI_INTERRUPT_LINE, 9);
+
+ pm_io_base = PM_IO_BASE;
+ smb_io_base = SMB_IO_BASE;
+ pm_sci_int = pci_config_readb(d, PCI_INTERRUPT_LINE);
+ piix4_pm_enable(d);
+ acpi_enabled = 1;
+ }
+}
+
+void pci_for_each_device(void (*init_func)(PCIDevice *d))
+{
+ PCIDevice d1, *d = &d1;
+ int bus, devfn;
+ uint16_t vendor_id, device_id;
+
+ for(bus = 0; bus < 1; bus++) {
+ for(devfn = 0; devfn < 256; devfn++) {
+ d->bus = bus;
+ d->devfn = devfn;
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+ if (vendor_id != 0xffff || device_id != 0xffff) {
+ init_func(d);
+ }
+ }
+ }
+}
+
+void pci_bios_init(void)
+{
+ pci_bios_io_addr = 0xc000;
+ pci_bios_mem_addr = 0xf0000000;
+ pci_bios_bigmem_addr = ram_size;
+ if (pci_bios_bigmem_addr < 0x90000000)
+ pci_bios_bigmem_addr = 0x90000000;
+
+ pci_for_each_device(pci_bios_init_bridges);
+
+ pci_for_each_device(pci_bios_init_device);
+}
+
+/****************************************************/
+/* Multi Processor table init */
+
+static void putb(uint8_t **pp, int val)
+{
+ uint8_t *q;
+ q = *pp;
+ *q++ = val;
+ *pp = q;
+}
+
+static void putstr(uint8_t **pp, const char *str)
+{
+ uint8_t *q;
+ q = *pp;
+ while (*str)
+ *q++ = *str++;
+ *pp = q;
+}
+
+static void putle16(uint8_t **pp, int val)
+{
+ uint8_t *q;
+ q = *pp;
+ *q++ = val;
+ *q++ = val >> 8;
+ *pp = q;
+}
+
+static void putle32(uint8_t **pp, int val)
+{
+ uint8_t *q;
+ q = *pp;
+ *q++ = val;
+ *q++ = val >> 8;
+ *q++ = val >> 16;
+ *q++ = val >> 24;
+ *pp = q;
+}
+
+static int mpf_checksum(const uint8_t *data, int len)
+{
+ int sum, i;
+ sum = 0;
+ for(i = 0; i < len; i++)
+ sum += data[i];
+ return sum & 0xff;
+}
+
+static unsigned long align(unsigned long addr, unsigned long v)
+{
+ return (addr + v - 1) & ~(v - 1);
+}
+
+static void mptable_init(void)
+{
+ uint8_t *mp_config_table, *q, *float_pointer_struct;
+ int ioapic_id, i, len;
+ int mp_config_table_size;
+
+#ifdef BX_USE_EBDA_TABLES
+ mp_config_table = (uint8_t *)(ram_size - ACPI_DATA_SIZE - MPTABLE_MAX_SIZE);
+#else
+ bios_table_cur_addr = align(bios_table_cur_addr, 16);
+ mp_config_table = (uint8_t *)bios_table_cur_addr;
+#endif
+ q = mp_config_table;
+ putstr(&q, "PCMP"); /* "PCMP signature */
+ putle16(&q, 0); /* table length (patched later) */
+ putb(&q, 4); /* spec rev */
+ putb(&q, 0); /* checksum (patched later) */
+#ifdef BX_QEMU
+ putstr(&q, "QEMUCPU "); /* OEM id */
+#else
+ putstr(&q, "BOCHSCPU");
+#endif
+ putstr(&q, "0.1 "); /* vendor id */
+ putle32(&q, 0); /* OEM table ptr */
+ putle16(&q, 0); /* OEM table size */
+ putle16(&q, MAX_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 < MAX_CPUS ; i++) {
+ putb(&q, 0); /* entry type = processor */
+ putb(&q, i); /* APIC id */
+ putb(&q, 0x11); /* local APIC version number */
+ if (i == 0)
+ putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */
+ else if ( i < smp_cpus)
+ putb(&q, 1); /* cpu flags: enabled */
+ else
+ putb(&q, 0); /* cpu flags: disabled */
+ putb(&q, 0); /* cpu signature */
+ putb(&q, 6);
+ putb(&q, 0);
+ putb(&q, 0);
+ putle16(&q, 0x201); /* feature flags */
+ putle16(&q, 0);
+
+ putle16(&q, 0); /* reserved */
+ putle16(&q, 0);
+ putle16(&q, 0);
+ putle16(&q, 0);
+ }
+
+ /* isa bus */
+ putb(&q, 1); /* entry type = bus */
+ putb(&q, 0); /* bus ID */
+ putstr(&q, "ISA ");
+
+ /* ioapic */
+ ioapic_id = smp_cpus;
+ putb(&q, 2); /* entry type = I/O APIC */
+ putb(&q, ioapic_id); /* apic ID */
+ putb(&q, 0x11); /* I/O APIC version number */
+ putb(&q, 1); /* enable */
+ putle32(&q, 0xfec00000); /* I/O APIC addr */
+
+ /* irqs */
+ for(i = 0; i < 16; i++) {
+ 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) */
+
+/*
+ * All tables must be byte-packed to match the ACPI specification, since
+ * the tables are provided by the system BIOS.
+ */
+
+#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \
+ uint8_t signature [4]; /* ACPI signature (4 ASCII characters) */\
+ uint32_t length; /* Length of table, in bytes, including header */\
+ uint8_t revision; /* ACPI Specification minor version # */\
+ uint8_t checksum; /* To make sum of entire table == 0 */\
+ uint8_t oem_id [6]; /* OEM identification */\
+ uint8_t oem_table_id [8]; /* OEM table identification */\
+ uint32_t oem_revision; /* OEM revision number */\
+ uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */\
+ uint32_t asl_compiler_revision; /* ASL compiler revision number */
+
+
+struct acpi_table_header /* ACPI common table header */
+{
+ ACPI_TABLE_HEADER_DEF
+} __attribute__((__packed__));
+
+struct rsdp_descriptor /* Root System Descriptor Pointer */
+{
+ uint8_t signature [8]; /* ACPI signature, contains "RSD PTR " */
+ uint8_t checksum; /* To make sum of struct == 0 */
+ uint8_t oem_id [6]; /* OEM identification */
+ uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */
+ uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */
+ uint32_t length; /* XSDT Length in bytes including hdr */
+ uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */
+ uint8_t extended_checksum; /* Checksum of entire table */
+ uint8_t reserved [3]; /* Reserved field must be 0 */
+} __attribute__((__packed__));
+
+/*
+ * 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 */
+} __attribute__((__packed__));
+
+/*
+ * ACPI 1.0 Firmware ACPI Control Structure (FACS)
+ */
+struct facs_descriptor_rev1
+{
+ uint8_t signature[4]; /* ACPI Signature */
+ uint32_t length; /* Length of structure, in bytes */
+ uint32_t hardware_signature; /* Hardware configuration signature */
+ uint32_t firmware_waking_vector; /* ACPI OS waking vector */
+ uint32_t global_lock; /* Global Lock */
+ uint32_t S4bios_f : 1; /* Indicates if S4BIOS support is present */
+ uint32_t reserved1 : 31; /* Must be 0 */
+ uint8_t resverved3 [40]; /* Reserved - must be zero */
+} __attribute__((__packed__));
+
+
+/*
+ * ACPI 1.0 Fixed ACPI Description Table (FADT)
+ */
+struct fadt_descriptor_rev1
+{
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t firmware_ctrl; /* Physical address of FACS */
+ uint32_t dsdt; /* Physical address of DSDT */
+ uint8_t model; /* System Interrupt Model */
+ uint8_t reserved1; /* Reserved */
+ uint16_t sci_int; /* System vector of SCI interrupt */
+ uint32_t smi_cmd; /* Port address of SMI command port */
+ uint8_t acpi_enable; /* Value to write to smi_cmd to enable ACPI */
+ uint8_t acpi_disable; /* Value to write to smi_cmd to disable ACPI */
+ uint8_t S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */
+ uint8_t reserved2; /* Reserved - must be zero */
+ uint32_t pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */
+ uint32_t pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */
+ uint32_t pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */
+ uint32_t pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */
+ uint32_t pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */
+ uint32_t pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */
+ uint32_t gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */
+ uint32_t gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */
+ uint8_t pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */
+ uint8_t pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */
+ uint8_t pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */
+ uint8_t pm_tmr_len; /* Byte Length of ports at pm_tm_blk */
+ uint8_t gpe0_blk_len; /* Byte Length of ports at gpe0_blk */
+ uint8_t gpe1_blk_len; /* Byte Length of ports at gpe1_blk */
+ uint8_t gpe1_base; /* Offset in gpe model where gpe1 events start */
+ uint8_t reserved3; /* Reserved */
+ uint16_t plvl2_lat; /* Worst case HW latency to enter/exit C2 state */
+ uint16_t plvl3_lat; /* Worst case HW latency to enter/exit C3 state */
+ uint16_t flush_size; /* Size of area read to flush caches */
+ uint16_t flush_stride; /* Stride used in flushing caches */
+ uint8_t duty_offset; /* Bit location of duty cycle field in p_cnt reg */
+ uint8_t duty_width; /* Bit width of duty cycle field in p_cnt reg */
+ uint8_t day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */
+ uint8_t mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */
+ uint8_t century; /* Index to century in RTC CMOS RAM */
+ uint8_t reserved4; /* Reserved */
+ uint8_t reserved4a; /* Reserved */
+ uint8_t reserved4b; /* Reserved */
+#if 0
+ uint32_t wb_invd : 1; /* The wbinvd instruction works properly */
+ uint32_t wb_invd_flush : 1; /* The wbinvd flushes but does not invalidate */
+ uint32_t proc_c1 : 1; /* All processors support C1 state */
+ uint32_t plvl2_up : 1; /* C2 state works on MP system */
+ uint32_t pwr_button : 1; /* Power button is handled as a generic feature */
+ uint32_t sleep_button : 1; /* Sleep button is handled as a generic feature, or not present */
+ uint32_t fixed_rTC : 1; /* RTC wakeup stat not in fixed register space */
+ uint32_t rtcs4 : 1; /* RTC wakeup stat not possible from S4 */
+ uint32_t tmr_val_ext : 1; /* The tmr_val width is 32 bits (0 = 24 bits) */
+ uint32_t reserved5 : 23; /* Reserved - must be zero */
+#else
+ uint32_t flags;
+#endif
+} __attribute__((__packed__));
+
+/*
+ * MADT values and structures
+ */
+
+/* Values for MADT PCATCompat */
+
+#define DUAL_PIC 0
+#define MULTIPLE_APIC 1
+
+
+/* Master MADT */
+
+struct multiple_apic_table
+{
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ uint32_t local_apic_address; /* Physical address of local APIC */
+#if 0
+ uint32_t PCATcompat : 1; /* A one indicates system also has dual 8259s */
+ uint32_t reserved1 : 31;
+#else
+ uint32_t flags;
+#endif
+} __attribute__((__packed__));
+
+
+/* Values for Type in APIC_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
+} __attribute__((__packed__));
+
+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 */
+} __attribute__((__packed__));
+
+struct madt_intsrcovr {
+ APIC_HEADER_DEF
+ uint8_t bus;
+ uint8_t source;
+ uint32_t gsi;
+ uint16_t flags;
+} __attribute__((packed));
+
+#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, uint8_t rev)
+{
+ memcpy(h->signature, sig, 4);
+ h->length = cpu_to_le32(len);
+ h->revision = rev;
+#ifdef BX_QEMU
+ memcpy(h->oem_id, "QEMU ", 6);
+ memcpy(h->oem_table_id, "QEMU", 4);
+#else
+ memcpy(h->oem_id, "BOCHS ", 6);
+ memcpy(h->oem_table_id, "BXPC", 4);
+#endif
+ memcpy(h->oem_table_id + 4, sig, 4);
+ h->oem_revision = cpu_to_le32(1);
+#ifdef BX_QEMU
+ memcpy(h->asl_compiler_id, "QEMU", 4);
+#else
+ memcpy(h->asl_compiler_id, "BXPC", 4);
+#endif
+ h->asl_compiler_revision = cpu_to_le32(1);
+ h->checksum = acpi_checksum((void *)h, len);
+}
+
+/* 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) * MAX_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 */
+ memset(rsdt, 0, sizeof(*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), 1);
+
+ /* FADT */
+ memset(fadt, 0, sizeof(*fadt));
+ fadt->firmware_ctrl = cpu_to_le32(facs_addr);
+ fadt->dsdt = cpu_to_le32(dsdt_addr);
+ fadt->model = 1;
+ fadt->reserved1 = 0;
+ fadt->sci_int = cpu_to_le16(pm_sci_int);
+ fadt->smi_cmd = cpu_to_le32(SMI_CMD_IO_ADDR);
+ fadt->acpi_enable = 0xf1;
+ fadt->acpi_disable = 0xf0;
+ fadt->pm1a_evt_blk = cpu_to_le32(pm_io_base);
+ fadt->pm1a_cnt_blk = cpu_to_le32(pm_io_base + 0x04);
+ fadt->pm_tmr_blk = cpu_to_le32(pm_io_base + 0x08);
+ fadt->pm1_evt_len = 4;
+ fadt->pm1_cnt_len = 2;
+ fadt->pm_tmr_len = 4;
+ fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported
+ fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported
+ fadt->gpe0_blk = cpu_to_le32(0xafe0);
+ fadt->gpe0_blk_len = 4;
+ /* WBINVD + PROC_C1 + SLP_BUTTON + FIX_RTC */
+ fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 6));
+ acpi_build_table_header((struct acpi_table_header *)fadt, "FACP",
+ sizeof(*fadt), 1);
+
+ /* FACS */
+ memset(facs, 0, sizeof(*facs));
+ memcpy(facs->signature, "FACS", 4);
+ facs->length = cpu_to_le32(sizeof(*facs));
+ BX_INFO("Firmware waking vector %p\n", &facs->firmware_waking_vector);
+
+ /* DSDT */
+ memcpy(dsdt, AmlCode, sizeof(AmlCode));
+
+ /* MADT */
+ {
+ struct madt_processor_apic *apic;
+ struct madt_io_apic *io_apic;
+ struct madt_intsrcovr *intsrcovr;
+
+ 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<MAX_CPUS;i++) {
+ apic->type = APIC_PROCESSOR;
+ apic->length = sizeof(*apic);
+ apic->processor_id = i;
+ apic->local_apic_id = i;
+ if (i < smp_cpus)
+ apic->flags = cpu_to_le32(1);
+ else
+ apic->flags = 0;
+ apic++;
+ }
+ io_apic = (void *)apic;
+ io_apic->type = APIC_IO;
+ io_apic->length = sizeof(*io_apic);
+ io_apic->io_apic_id = smp_cpus;
+ io_apic->address = cpu_to_le32(0xfec00000);
+ io_apic->interrupt = cpu_to_le32(0);
+
+ intsrcovr = (struct madt_intsrcovr*)(io_apic + 1);
+ for ( i = 0; i < 16; i++ ) {
+ if ( PCI_ISA_IRQ_MASK & (1U << i) ) {
+ memset(intsrcovr, 0, sizeof(*intsrcovr));
+ intsrcovr->type = APIC_XRUPT_OVERRIDE;
+ intsrcovr->length = sizeof(*intsrcovr);
+ intsrcovr->source = i;
+ intsrcovr->gsi = i;
+ intsrcovr->flags = 0xd; /* active high, level triggered */
+ } else {
+ /* No need for a INT source override structure. */
+ continue;
+ }
+ intsrcovr++;
+ madt_size += sizeof(struct madt_intsrcovr);
+ }
+ acpi_build_table_header((struct acpi_table_header *)madt,
+ "APIC", madt_size, 1);
+ }
+}
+
+/* SMBIOS entry point -- must be written to a 16-bit aligned address
+ between 0xf0000 and 0xfffff.
+ */
+struct smbios_entry_point {
+ char anchor_string[4];
+ uint8_t checksum;
+ uint8_t length;
+ uint8_t smbios_major_version;
+ uint8_t smbios_minor_version;
+ uint16_t max_structure_size;
+ uint8_t entry_point_revision;
+ uint8_t formatted_area[5];
+ char intermediate_anchor_string[5];
+ uint8_t intermediate_checksum;
+ uint16_t structure_table_length;
+ uint32_t structure_table_address;
+ uint16_t number_of_structures;
+ uint8_t smbios_bcd_revision;
+} __attribute__((__packed__));
+
+/* This goes at the beginning of every SMBIOS structure. */
+struct smbios_structure_header {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+} __attribute__((__packed__));
+
+/* SMBIOS type 0 - BIOS Information */
+struct smbios_type_0 {
+ struct smbios_structure_header header;
+ uint8_t vendor_str;
+ uint8_t bios_version_str;
+ uint16_t bios_starting_address_segment;
+ uint8_t bios_release_date_str;
+ uint8_t bios_rom_size;
+ uint8_t bios_characteristics[8];
+ uint8_t bios_characteristics_extension_bytes[2];
+ uint8_t system_bios_major_release;
+ uint8_t system_bios_minor_release;
+ uint8_t embedded_controller_major_release;
+ uint8_t embedded_controller_minor_release;
+} __attribute__((__packed__));
+
+/* SMBIOS type 1 - System Information */
+struct smbios_type_1 {
+ struct smbios_structure_header header;
+ uint8_t manufacturer_str;
+ uint8_t product_name_str;
+ uint8_t version_str;
+ uint8_t serial_number_str;
+ uint8_t uuid[16];
+ uint8_t wake_up_type;
+ uint8_t sku_number_str;
+ uint8_t family_str;
+} __attribute__((__packed__));
+
+/* SMBIOS type 3 - System Enclosure (v2.3) */
+struct smbios_type_3 {
+ struct smbios_structure_header header;
+ uint8_t manufacturer_str;
+ uint8_t type;
+ uint8_t version_str;
+ uint8_t serial_number_str;
+ uint8_t asset_tag_number_str;
+ uint8_t boot_up_state;
+ uint8_t power_supply_state;
+ uint8_t thermal_state;
+ uint8_t security_status;
+ uint32_t oem_defined;
+ uint8_t height;
+ uint8_t number_of_power_cords;
+ uint8_t contained_element_count;
+ // contained elements follow
+} __attribute__((__packed__));
+
+/* SMBIOS type 4 - Processor Information (v2.0) */
+struct smbios_type_4 {
+ struct smbios_structure_header header;
+ uint8_t socket_designation_str;
+ uint8_t processor_type;
+ uint8_t processor_family;
+ uint8_t processor_manufacturer_str;
+ uint32_t processor_id[2];
+ uint8_t processor_version_str;
+ uint8_t voltage;
+ uint16_t external_clock;
+ uint16_t max_speed;
+ uint16_t current_speed;
+ uint8_t status;
+ uint8_t processor_upgrade;
+ uint16_t l1_cache_handle;
+ uint16_t l2_cache_handle;
+ uint16_t l3_cache_handle;
+} __attribute__((__packed__));
+
+/* SMBIOS type 16 - Physical Memory Array
+ * Associated with one type 17 (Memory Device).
+ */
+struct smbios_type_16 {
+ struct smbios_structure_header header;
+ uint8_t location;
+ uint8_t use;
+ uint8_t error_correction;
+ uint32_t maximum_capacity;
+ uint16_t memory_error_information_handle;
+ uint16_t number_of_memory_devices;
+} __attribute__((__packed__));
+
+/* SMBIOS type 17 - Memory Device
+ * Associated with one type 19
+ */
+struct smbios_type_17 {
+ struct smbios_structure_header header;
+ uint16_t physical_memory_array_handle;
+ uint16_t memory_error_information_handle;
+ uint16_t total_width;
+ uint16_t data_width;
+ uint16_t size;
+ uint8_t form_factor;
+ uint8_t device_set;
+ uint8_t device_locator_str;
+ uint8_t bank_locator_str;
+ uint8_t memory_type;
+ uint16_t type_detail;
+} __attribute__((__packed__));
+
+/* SMBIOS type 19 - Memory Array Mapped Address */
+struct smbios_type_19 {
+ struct smbios_structure_header header;
+ uint32_t starting_address;
+ uint32_t ending_address;
+ uint16_t memory_array_handle;
+ uint8_t partition_width;
+} __attribute__((__packed__));
+
+/* SMBIOS type 20 - Memory Device Mapped Address */
+struct smbios_type_20 {
+ struct smbios_structure_header header;
+ uint32_t starting_address;
+ uint32_t ending_address;
+ uint16_t memory_device_handle;
+ uint16_t memory_array_mapped_address_handle;
+ uint8_t partition_row_position;
+ uint8_t interleave_position;
+ uint8_t interleaved_data_depth;
+} __attribute__((__packed__));
+
+/* SMBIOS type 32 - System Boot Information */
+struct smbios_type_32 {
+ struct smbios_structure_header header;
+ uint8_t reserved[6];
+ uint8_t boot_status;
+} __attribute__((__packed__));
+
+/* SMBIOS type 127 -- End-of-table */
+struct smbios_type_127 {
+ struct smbios_structure_header header;
+} __attribute__((__packed__));
+
+static void
+smbios_entry_point_init(void *start,
+ uint16_t max_structure_size,
+ uint16_t structure_table_length,
+ uint32_t structure_table_address,
+ uint16_t number_of_structures)
+{
+ uint8_t sum;
+ int i;
+ struct smbios_entry_point *ep = (struct smbios_entry_point *)start;
+
+ memcpy(ep->anchor_string, "_SM_", 4);
+ ep->length = 0x1f;
+ ep->smbios_major_version = 2;
+ ep->smbios_minor_version = 4;
+ ep->max_structure_size = max_structure_size;
+ ep->entry_point_revision = 0;
+ memset(ep->formatted_area, 0, 5);
+ memcpy(ep->intermediate_anchor_string, "_DMI_", 5);
+
+ ep->structure_table_length = structure_table_length;
+ ep->structure_table_address = structure_table_address;
+ ep->number_of_structures = number_of_structures;
+ ep->smbios_bcd_revision = 0x24;
+
+ ep->checksum = 0;
+ ep->intermediate_checksum = 0;
+
+ sum = 0;
+ for (i = 0; i < 0x10; i++)
+ sum += ((int8_t *)start)[i];
+ ep->checksum = -sum;
+
+ sum = 0;
+ for (i = 0x10; i < ep->length; i++)
+ sum += ((int8_t *)start)[i];
+ ep->intermediate_checksum = -sum;
+ }
+
+/* Type 0 -- BIOS Information */
+#define RELEASE_DATE_STR "01/01/2007"
+static void *
+smbios_type_0_init(void *start)
+{
+ struct smbios_type_0 *p = (struct smbios_type_0 *)start;
+
+ p->header.type = 0;
+ p->header.length = sizeof(struct smbios_type_0);
+ p->header.handle = 0;
+
+ p->vendor_str = 1;
+ p->bios_version_str = 1;
+ p->bios_starting_address_segment = 0xe800;
+ p->bios_release_date_str = 2;
+ p->bios_rom_size = 0; /* FIXME */
+
+ memset(p->bios_characteristics, 0, 8);
+ p->bios_characteristics[0] = 0x08; /* BIOS characteristics not supported */
+ p->bios_characteristics_extension_bytes[0] = 0;
+ p->bios_characteristics_extension_bytes[1] = 0;
+
+ p->system_bios_major_release = 1;
+ p->system_bios_minor_release = 0;
+ p->embedded_controller_major_release = 0xff;
+ p->embedded_controller_minor_release = 0xff;
+
+ start += sizeof(struct smbios_type_0);
+ memcpy((char *)start, BX_APPNAME, sizeof(BX_APPNAME));
+ start += sizeof(BX_APPNAME);
+ memcpy((char *)start, RELEASE_DATE_STR, sizeof(RELEASE_DATE_STR));
+ start += sizeof(RELEASE_DATE_STR);
+ *((uint8_t *)start) = 0;
+
+ return start+1;
+}
+
+/* Type 1 -- System Information */
+static void *
+smbios_type_1_init(void *start)
+{
+ struct smbios_type_1 *p = (struct smbios_type_1 *)start;
+ p->header.type = 1;
+ p->header.length = sizeof(struct smbios_type_1);
+ p->header.handle = 0x100;
+
+ p->manufacturer_str = 0;
+ p->product_name_str = 0;
+ p->version_str = 0;
+ p->serial_number_str = 0;
+
+ memcpy(p->uuid, bios_uuid, 16);
+
+ p->wake_up_type = 0x06; /* power switch */
+ p->sku_number_str = 0;
+ p->family_str = 0;
+
+ start += sizeof(struct smbios_type_1);
+ *((uint16_t *)start) = 0;
+
+ return start+2;
+}
+
+/* Type 3 -- System Enclosure */
+static void *
+smbios_type_3_init(void *start)
+{
+ struct smbios_type_3 *p = (struct smbios_type_3 *)start;
+
+ p->header.type = 3;
+ p->header.length = sizeof(struct smbios_type_3);
+ p->header.handle = 0x300;
+
+ p->manufacturer_str = 0;
+ p->type = 0x01; /* other */
+ p->version_str = 0;
+ p->serial_number_str = 0;
+ p->asset_tag_number_str = 0;
+ p->boot_up_state = 0x03; /* safe */
+ p->power_supply_state = 0x03; /* safe */
+ p->thermal_state = 0x03; /* safe */
+ p->security_status = 0x02; /* unknown */
+ p->oem_defined = 0;
+ p->height = 0;
+ p->number_of_power_cords = 0;
+ p->contained_element_count = 0;
+
+ start += sizeof(struct smbios_type_3);
+ *((uint16_t *)start) = 0;
+
+ return start+2;
+}
+
+/* Type 4 -- Processor Information */
+static void *
+smbios_type_4_init(void *start, unsigned int cpu_number)
+{
+ struct smbios_type_4 *p = (struct smbios_type_4 *)start;
+
+ p->header.type = 4;
+ p->header.length = sizeof(struct smbios_type_4);
+ p->header.handle = 0x400 + cpu_number;
+
+ p->socket_designation_str = 1;
+ p->processor_type = 0x03; /* CPU */
+ p->processor_family = 0x01; /* other */
+ p->processor_manufacturer_str = 0;
+
+ p->processor_id[0] = cpuid_signature;
+ p->processor_id[1] = cpuid_features;
+
+ p->processor_version_str = 0;
+ p->voltage = 0;
+ p->external_clock = 0;
+
+ p->max_speed = 0; /* unknown */
+ p->current_speed = 0; /* unknown */
+
+ p->status = 0x41; /* socket populated, CPU enabled */
+ p->processor_upgrade = 0x01; /* other */
+
+ p->l1_cache_handle = 0xffff; /* cache information structure not provided */
+ p->l2_cache_handle = 0xffff;
+ p->l3_cache_handle = 0xffff;
+
+ start += sizeof(struct smbios_type_4);
+
+ memcpy((char *)start, "CPU " "\0" "" "\0" "", 7);
+ ((char *)start)[4] = cpu_number + '0';
+
+ return start+7;
+}
+
+/* Type 16 -- Physical Memory Array */
+static void *
+smbios_type_16_init(void *start, uint32_t memsize, int nr_mem_devs)
+{
+ struct smbios_type_16 *p = (struct smbios_type_16*)start;
+
+ p->header.type = 16;
+ p->header.length = sizeof(struct smbios_type_16);
+ p->header.handle = 0x1000;
+
+ p->location = 0x01; /* other */
+ p->use = 0x03; /* system memory */
+ p->error_correction = 0x01; /* other */
+ p->maximum_capacity = memsize * 1024;
+ p->memory_error_information_handle = 0xfffe; /* none provided */
+ p->number_of_memory_devices = nr_mem_devs;
+
+ start += sizeof(struct smbios_type_16);
+ *((uint16_t *)start) = 0;
+
+ return start + 2;
+}
+
+/* Type 17 -- Memory Device */
+static void *
+smbios_type_17_init(void *start, uint32_t memory_size_mb, int instance)
+{
+ struct smbios_type_17 *p = (struct smbios_type_17 *)start;
+
+ p->header.type = 17;
+ p->header.length = sizeof(struct smbios_type_17);
+ p->header.handle = 0x1100 + instance;
+
+ p->physical_memory_array_handle = 0x1000;
+ p->total_width = 64;
+ p->data_width = 64;
+/* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */
+ p->size = memory_size_mb;
+ p->form_factor = 0x09; /* DIMM */
+ p->device_set = 0;
+ p->device_locator_str = 1;
+ p->bank_locator_str = 0;
+ p->memory_type = 0x07; /* RAM */
+ p->type_detail = 0;
+
+ start += sizeof(struct smbios_type_17);
+ snprintf(start, 8, "DIMM %d", instance);
+ start += strlen(start) + 1;
+ *((uint8_t *)start) = 0;
+
+ return start+1;
+}
+
+/* Type 19 -- Memory Array Mapped Address */
+static void *
+smbios_type_19_init(void *start, uint32_t memory_size_mb, int instance)
+{
+ struct smbios_type_19 *p = (struct smbios_type_19 *)start;
+
+ p->header.type = 19;
+ p->header.length = sizeof(struct smbios_type_19);
+ p->header.handle = 0x1300 + instance;
+
+ p->starting_address = instance << 24;
+ p->ending_address = p->starting_address + (memory_size_mb << 10) - 1;
+ p->memory_array_handle = 0x1000;
+ p->partition_width = 1;
+
+ start += sizeof(struct smbios_type_19);
+ *((uint16_t *)start) = 0;
+
+ return start + 2;
+}
+
+/* Type 20 -- Memory Device Mapped Address */
+static void *
+smbios_type_20_init(void *start, uint32_t memory_size_mb, int instance)
+{
+ struct smbios_type_20 *p = (struct smbios_type_20 *)start;
+
+ p->header.type = 20;
+ p->header.length = sizeof(struct smbios_type_20);
+ p->header.handle = 0x1400 + instance;
+
+ p->starting_address = instance << 24;
+ p->ending_address = p->starting_address + (memory_size_mb << 10) - 1;
+ p->memory_device_handle = 0x1100 + instance;
+ p->memory_array_mapped_address_handle = 0x1300 + instance;
+ p->partition_row_position = 1;
+ p->interleave_position = 0;
+ p->interleaved_data_depth = 0;
+
+ start += sizeof(struct smbios_type_20);
+
+ *((uint16_t *)start) = 0;
+ return start+2;
+}
+
+/* Type 32 -- System Boot Information */
+static void *
+smbios_type_32_init(void *start)
+{
+ struct smbios_type_32 *p = (struct smbios_type_32 *)start;
+
+ p->header.type = 32;
+ p->header.length = sizeof(struct smbios_type_32);
+ p->header.handle = 0x2000;
+ memset(p->reserved, 0, 6);
+ p->boot_status = 0; /* no errors detected */
+
+ start += sizeof(struct smbios_type_32);
+ *((uint16_t *)start) = 0;
+
+ return start+2;
+}
+
+/* Type 127 -- End of Table */
+static void *
+smbios_type_127_init(void *start)
+{
+ struct smbios_type_127 *p = (struct smbios_type_127 *)start;
+
+ p->header.type = 127;
+ p->header.length = sizeof(struct smbios_type_127);
+ p->header.handle = 0x7f00;
+
+ start += sizeof(struct smbios_type_127);
+ *((uint16_t *)start) = 0;
+
+ return start + 2;
+}
+
+void smbios_init(void)
+{
+ unsigned cpu_num, nr_structs = 0, max_struct_size = 0;
+ char *start, *p, *q;
+ int memsize = (ram_end == ram_size) ? ram_size / (1024 * 1024) :
+ (ram_end - (1ull << 32) + ram_size) / (1024 * 1024);
+ int i, nr_mem_devs;
+
+#ifdef BX_USE_EBDA_TABLES
+ ebda_cur_addr = align(ebda_cur_addr, 16);
+ start = (void *)(ebda_cur_addr);
+#else
+ bios_table_cur_addr = align(bios_table_cur_addr, 16);
+ start = (void *)(bios_table_cur_addr);
+#endif
+
+ p = (char *)start + sizeof(struct smbios_entry_point);
+
+#define add_struct(fn) do{ \
+ q = (fn); \
+ nr_structs++; \
+ if ((q - p) > max_struct_size) \
+ max_struct_size = q - p; \
+ p = q; \
+}while (0)
+
+ add_struct(smbios_type_0_init(p));
+ add_struct(smbios_type_1_init(p));
+ add_struct(smbios_type_3_init(p));
+ for (cpu_num = 1; cpu_num <= smp_cpus; cpu_num++)
+ add_struct(smbios_type_4_init(p, cpu_num));
+
+ /* Each 'memory device' covers up to 16GB of address space. */
+ nr_mem_devs = (memsize + 0x3fff) >> 14;
+ add_struct(smbios_type_16_init(p, memsize, nr_mem_devs));
+ for ( i = 0; i < nr_mem_devs; i++ )
+ {
+ uint32_t dev_memsize = ((i == (nr_mem_devs - 1))
+ ? (memsize & 0x3fff) : 0x4000);
+ add_struct(smbios_type_17_init(p, dev_memsize, i));
+ add_struct(smbios_type_19_init(p, dev_memsize, i));
+ add_struct(smbios_type_20_init(p, dev_memsize, i));
+ }
+
+ add_struct(smbios_type_32_init(p));
+ add_struct(smbios_type_127_init(p));
+
+#undef add_struct
+
+ smbios_entry_point_init(
+ start, max_struct_size,
+ (p - (char *)start) - sizeof(struct smbios_entry_point),
+ (uint32_t)(start + sizeof(struct smbios_entry_point)),
+ nr_structs);
+
+#ifdef BX_USE_EBDA_TABLES
+ ebda_cur_addr += (p - (char *)start);
+#else
+ bios_table_cur_addr += (p - (char *)start);
+#endif
+
+ BX_INFO("SMBIOS table addr=0x%08lx\n", (unsigned long)start);
+}
+
+static uint32_t find_resume_vector(void)
+{
+ unsigned long addr, start, end;
+
+#ifdef BX_USE_EBDA_TABLES
+ start = align(ebda_cur_addr, 16);
+ end = 0xa000 << 4;
+#else
+ if (bios_table_cur_addr == 0)
+ return 0;
+ start = align(bios_table_cur_addr, 16);
+ end = bios_table_end_addr;
+#endif
+
+ for (addr = start; addr < end; addr += 16) {
+ if (!memcmp((void*)addr, "RSD PTR ", 8)) {
+ struct rsdp_descriptor *rsdp = (void*)addr;
+ struct rsdt_descriptor_rev1 *rsdt = (void*)rsdp->rsdt_physical_address;
+ struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[0];
+ struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl;
+ return facs->firmware_waking_vector;
+ }
+ }
+
+ return 0;
+}
+
+static void find_440fx(PCIDevice *d)
+{
+ uint16_t vendor_id, device_id;
+
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82441)
+ i440_pcidev = *d;
+}
+
+static void reinit_piix4_pm(PCIDevice *d)
+{
+ uint16_t vendor_id, device_id;
+
+ vendor_id = pci_config_readw(d, PCI_VENDOR_ID);
+ device_id = pci_config_readw(d, PCI_DEVICE_ID);
+
+ if (vendor_id == PCI_VENDOR_ID_INTEL && device_id == PCI_DEVICE_ID_INTEL_82371AB_3)
+ piix4_pm_enable(d);
+}
+
+void rombios32_init(uint32_t *s3_resume_vector, uint8_t *shutdown_flag)
+{
+ BX_INFO("Starting rombios32\n");
+ BX_INFO("Shutdown flag %x\n", *shutdown_flag);
+
+#ifdef BX_QEMU
+ qemu_cfg_port = qemu_cfg_port_probe();
+#endif
+
+ init_smp_msrs();
+
+#ifdef BX_QEMU
+ qemu_cfg_port = qemu_cfg_port_probe();
+#endif
+
+ ram_probe();
+
+ cpu_probe();
+
+ smp_probe();
+
+ find_bios_table_area();
+
+ if (*shutdown_flag == 0xfe) {
+ /* redirect bios read access to RAM */
+ pci_for_each_device(find_440fx);
+ bios_lock_shadow_ram(); /* bios is already copied */
+ *s3_resume_vector = find_resume_vector();
+ if (!*s3_resume_vector) {
+ BX_INFO("This is S3 resume but wakeup vector is NULL\n");
+ } else {
+ BX_INFO("S3 resume vector %p\n", *s3_resume_vector);
+ pci_for_each_device(reinit_piix4_pm);
+ }
+ return;
+ }
+
+ pci_bios_init();
+
+ if (bios_table_cur_addr != 0) {
+
+ mptable_init();
+
+ uuid_probe();
+
+ smbios_init();
+
+ if (acpi_enabled)
+ acpi_bios_init();
+
+ bios_lock_shadow_ram();
+
+ BX_INFO("bios_table_cur_addr: 0x%08lx\n", bios_table_cur_addr);
+ if (bios_table_cur_addr > bios_table_end_addr)
+ BX_PANIC("bios_table_end_addr overflow!\n");
+#ifdef BX_USE_EBDA_TABLES
+ BX_INFO("ebda_cur_addr: 0x%08lx\n", ebda_cur_addr);
+ if (ebda_cur_addr > 0xA0000)
+ BX_PANIC("ebda_cur_addr overflow!\n");
+#endif
+ }
+}
diff --git a/kvm/bios/rombios32.ld b/kvm/bios/rombios32.ld
new file mode 100644
index 000000000..1fc99c38f
--- /dev/null
+++ b/kvm/bios/rombios32.ld
@@ -0,0 +1,22 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start);
+SECTIONS
+{
+ . = 0x000e0000;
+ .text : { *(.text) }
+ .rodata : { *(.rodata*) }
+ . = ALIGN(64);
+ fixup_start = .;
+ .fixup : { *(.fixup) }
+ fixup_end = .;
+ . = ALIGN(4096);
+ _end = . ;
+ .data 0x700 : AT (_end) { __data_start = .; *(.data); __data_end = .;}
+ .bss : { __bss_start = .; *(.bss) *(COMMON); __bss_end = .;}
+ /DISCARD/ : { *(.stab)
+ *(.stabstr)
+ *(.comment)
+ *(.note)
+ }
+}
diff --git a/kvm/bios/rombios32start.S b/kvm/bios/rombios32start.S
new file mode 100644
index 000000000..335e3efdc
--- /dev/null
+++ b/kvm/bios/rombios32start.S
@@ -0,0 +1,123 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rombios32start.S,v 1.3 2006/10/02 06:29:38 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// 32 bit Bochs BIOS init code
+// Copyright (C) 2006 Fabrice Bellard
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#include "rombios.h"
+
+.globl _start
+.globl smp_ap_boot_code_start
+.globl smp_ap_boot_code_end
+.global smm_relocation_start
+.global smm_relocation_end
+.global smm_code_start
+.global smm_code_end
+
+_start:
+ /* clear bss section */
+ xor %eax, %eax
+ mov $__bss_start, %edi
+ mov $__bss_end, %ecx
+ sub %edi, %ecx
+ rep stosb
+
+ /* copy data section */
+ mov $_end, %esi
+ mov $__data_start, %edi
+ mov $__data_end, %ecx
+ sub %edi, %ecx
+ rep movsb
+
+ jmp rombios32_init
+
+ .code16
+smp_ap_boot_code_start:
+ cli
+ xor %ax, %ax
+ mov %ax, %ds
+ xor %eax, %eax
+ xor %edx, %edx
+ mov $0x10, %ecx
+ wrmsr
+
+ mov $SMP_MSR_ADDR, %ebx
+11:
+ mov 0(%ebx), %ecx
+ test %ecx, %ecx
+ jz 12f
+ mov 4(%ebx), %eax
+ mov 8(%ebx), %edx
+ wrmsr
+ add $12, %ebx
+ jmp 11b
+12:
+
+ lock incw smp_cpus
+1:
+ hlt
+ jmp 1b
+smp_ap_boot_code_end:
+
+/* code to relocate SMBASE to 0xa0000 */
+smm_relocation_start:
+ mov $0x38000 + 0x7efc, %ebx
+ addr32 mov (%ebx), %al /* revision ID to see if x86_64 or x86 */
+ cmp $0x64, %al
+ je 1f
+ mov $0x38000 + 0x7ef8, %ebx
+ jmp 2f
+1:
+ mov $0x38000 + 0x7f00, %ebx
+2:
+ movl $0xa0000, %eax
+ addr32 movl %eax, (%ebx)
+ /* indicate to the BIOS that the SMM code was executed */
+ mov $0x00, %al
+ movw $0xb3, %dx
+ outb %al, %dx
+ rsm
+smm_relocation_end:
+
+/* minimal SMM code to enable or disable ACPI */
+smm_code_start:
+ movw $0xb2, %dx
+ inb %dx, %al
+ cmp $0xf0, %al
+ jne 1f
+
+ /* ACPI disable */
+ mov $PM_IO_BASE + 0x04, %dx /* PMCNTRL */
+ inw %dx, %ax
+ andw $~1, %ax
+ outw %ax, %dx
+
+ jmp 2f
+
+1:
+ cmp $0xf1, %al
+ jne 2f
+
+ /* ACPI enable */
+ mov $PM_IO_BASE + 0x04, %dx /* PMCNTRL */
+ inw %dx, %ax
+ orw $1, %ax
+ outw %ax, %dx
+
+2:
+ rsm
+smm_code_end:
diff --git a/kvm/bios/usage.cc b/kvm/bios/usage.cc
new file mode 100644
index 000000000..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/bios/vapic.S b/kvm/bios/vapic.S
new file mode 100644
index 000000000..cf2a474d5
--- /dev/null
+++ b/kvm/bios/vapic.S
@@ -0,0 +1,294 @@
+ .text
+ .code32
+ .align 4096
+
+vapic_size = 2*4096
+
+.macro fixup delta=-4
+777:
+ .pushsection .fixup, "a"
+ .long 777b + \delta - vapic_base
+ .popsection
+.endm
+
+.macro reenable_vtpr
+ out %al, $0x7e
+.endm
+
+vapic_base:
+ .ascii "kvm aPiC"
+
+ /* relocation data */
+ .long vapic_base ; fixup
+ .long fixup_start ; fixup
+ .long fixup_end ; fixup
+
+ .long vapic ; fixup
+ .long vapic_size
+vcpu_shift:
+ .long 0
+real_tpr:
+ .long 0
+ .long up_set_tpr ; fixup
+ .long up_set_tpr_eax ; fixup
+ .long up_get_tpr_eax ; fixup
+ .long up_get_tpr_ecx ; fixup
+ .long up_get_tpr_edx ; fixup
+ .long up_get_tpr_ebx ; fixup
+ .long 0 /* esp. won't work. */
+ .long up_get_tpr_ebp ; fixup
+ .long up_get_tpr_esi ; fixup
+ .long up_get_tpr_edi ; fixup
+ .long up_get_tpr_stack ; fixup
+ .long mp_set_tpr ; fixup
+ .long mp_set_tpr_eax ; fixup
+ .long mp_get_tpr_eax ; fixup
+ .long mp_get_tpr_ecx ; fixup
+ .long mp_get_tpr_edx ; fixup
+ .long mp_get_tpr_ebx ; fixup
+ .long 0 /* esp. won't work. */
+ .long mp_get_tpr_ebp ; fixup
+ .long mp_get_tpr_esi ; fixup
+ .long mp_get_tpr_edi ; fixup
+ .long mp_get_tpr_stack ; fixup
+
+.macro kvm_hypercall
+ .byte 0x0f, 0x01, 0xc1
+.endm
+
+kvm_hypercall_vapic_poll_irq = 1
+
+pcr_cpu = 0x51
+
+.align 64
+
+mp_get_tpr_eax:
+ pushf
+ cli
+ reenable_vtpr
+ push %ecx
+
+ fs/movzbl pcr_cpu, %eax
+
+ mov vcpu_shift, %ecx ; fixup
+ shl %cl, %eax
+ testb $1, vapic+4(%eax) ; fixup delta=-5
+ jz mp_get_tpr_bad
+ movzbl vapic(%eax), %eax ; fixup
+
+mp_get_tpr_out:
+ pop %ecx
+ popf
+ ret
+
+mp_get_tpr_bad:
+ mov real_tpr, %eax ; fixup
+ mov (%eax), %eax
+ jmp mp_get_tpr_out
+
+mp_get_tpr_ebx:
+ mov %eax, %ebx
+ call mp_get_tpr_eax
+ xchg %eax, %ebx
+ ret
+
+mp_get_tpr_ecx:
+ mov %eax, %ecx
+ call mp_get_tpr_eax
+ xchg %eax, %ecx
+ ret
+
+mp_get_tpr_edx:
+ mov %eax, %edx
+ call mp_get_tpr_eax
+ xchg %eax, %edx
+ ret
+
+mp_get_tpr_esi:
+ mov %eax, %esi
+ call mp_get_tpr_eax
+ xchg %eax, %esi
+ ret
+
+mp_get_tpr_edi:
+ mov %eax, %edi
+ call mp_get_tpr_edi
+ xchg %eax, %edi
+ ret
+
+mp_get_tpr_ebp:
+ mov %eax, %ebp
+ call mp_get_tpr_eax
+ xchg %eax, %ebp
+ ret
+
+mp_get_tpr_stack:
+ call mp_get_tpr_eax
+ xchg %eax, 4(%esp)
+ ret
+
+mp_set_tpr_eax:
+ push %eax
+ call mp_set_tpr
+ ret
+
+mp_set_tpr:
+ pushf
+ push %eax
+ push %ecx
+ push %edx
+ push %ebx
+ cli
+ reenable_vtpr
+
+mp_set_tpr_failed:
+ fs/movzbl pcr_cpu, %edx
+
+ mov vcpu_shift, %ecx ; fixup
+ shl %cl, %edx
+
+ testb $1, vapic+4(%edx) ; fixup delta=-5
+ jz mp_set_tpr_bad
+
+ mov vapic(%edx), %eax ; fixup
+
+ mov %eax, %ebx
+ mov 24(%esp), %bl
+
+ /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
+
+ lock cmpxchg %ebx, vapic(%edx) ; fixup
+ jnz mp_set_tpr_failed
+
+ /* compute ppr */
+ cmp %bh, %bl
+ jae mp_tpr_is_bigger
+mp_isr_is_bigger:
+ mov %bh, %bl
+mp_tpr_is_bigger:
+ /* %bl = ppr */
+ mov %bl, %ch /* ch = ppr */
+ rol $8, %ebx
+ /* now: %bl = irr, %bh = ppr */
+ cmp %bh, %bl
+ ja mp_set_tpr_poll_irq
+
+mp_set_tpr_out:
+ pop %ebx
+ pop %edx
+ pop %ecx
+ pop %eax
+ popf
+ ret $4
+
+mp_set_tpr_poll_irq:
+ mov $kvm_hypercall_vapic_poll_irq, %eax
+ kvm_hypercall
+ jmp mp_set_tpr_out
+
+mp_set_tpr_bad:
+ mov 24(%esp), %ecx
+ mov real_tpr, %eax ; fixup
+ mov %ecx, (%eax)
+ jmp mp_set_tpr_out
+
+up_get_tpr_eax:
+ reenable_vtpr
+ movzbl vapic, %eax ; fixup
+ ret
+
+up_get_tpr_ebx:
+ reenable_vtpr
+ movzbl vapic, %ebx ; fixup
+ ret
+
+up_get_tpr_ecx:
+ reenable_vtpr
+ movzbl vapic, %ecx ; fixup
+ ret
+
+up_get_tpr_edx:
+ reenable_vtpr
+ movzbl vapic, %edx ; fixup
+ ret
+
+up_get_tpr_esi:
+ reenable_vtpr
+ movzbl vapic, %esi ; fixup
+ ret
+
+up_get_tpr_edi:
+ reenable_vtpr
+ movzbl vapic, %edi ; fixup
+ ret
+
+up_get_tpr_ebp:
+ reenable_vtpr
+ movzbl vapic, %ebp ; fixup
+ ret
+
+up_get_tpr_stack:
+ reenable_vtpr
+ movzbl vapic, %eax ; fixup
+ xchg %eax, 4(%esp)
+ ret
+
+up_set_tpr_eax:
+ push %eax
+ call up_set_tpr
+ ret
+
+up_set_tpr:
+ pushf
+ push %eax
+ push %ecx
+ push %ebx
+ reenable_vtpr
+
+up_set_tpr_failed:
+ mov vapic, %eax ; fixup
+
+ mov %eax, %ebx
+ mov 20(%esp), %bl
+
+ /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
+
+ lock cmpxchg %ebx, vapic ; fixup
+ jnz up_set_tpr_failed
+
+ /* compute ppr */
+ cmp %bh, %bl
+ jae up_tpr_is_bigger
+up_isr_is_bigger:
+ mov %bh, %bl
+up_tpr_is_bigger:
+ /* %bl = ppr */
+ mov %bl, %ch /* ch = ppr */
+ rol $8, %ebx
+ /* now: %bl = irr, %bh = ppr */
+ cmp %bh, %bl
+ ja up_set_tpr_poll_irq
+
+up_set_tpr_out:
+ pop %ebx
+ pop %ecx
+ pop %eax
+ popf
+ ret $4
+
+up_set_tpr_poll_irq:
+ mov $kvm_hypercall_vapic_poll_irq, %eax
+ kvm_hypercall
+ jmp up_set_tpr_out
+
+.align 4096
+/*
+ * vapic format:
+ * per-vcpu records of size 2^vcpu shift.
+ * byte 0: tpr (r/w)
+ * byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero
+ * byte 2: zero (r/o)
+ * byte 3: highest pending interrupt (irr) (r/o)
+ */
+vapic:
+. = . + vapic_size
diff --git a/kvm/configure b/kvm/configure
new file mode 100755
index 000000000..d2883a74a
--- /dev/null
+++ b/kvm/configure
@@ -0,0 +1,172 @@
+#!/bin/bash
+
+prefix=/usr/local
+kernelsourcedir=
+kerneldir=/lib/modules/$(uname -r)/build
+cc=gcc
+ld=ld
+objcopy=objcopy
+ar=ar
+want_module=1
+qemu_cflags=
+qemu_ldflags=
+kvm_trace=
+qemu_opts=()
+cross_prefix=
+arch=`uname -m`
+target_exec=
+# don't use uname if kerneldir is set
+no_uname=
+depmod_version=
+if [ -z "TMPDIR" ] ; then
+ TMPDIR=.
+fi
+
+usage() {
+ cat <<-EOF
+ Usage: $0 [options]
+
+ Options include:
+ --arch=ARCH architecture to compile for ($arch)
+ --cross-prefix=PREFIX prefix for cross compile
+ --prefix=PREFIX where to install things ($prefix)
+ --with-patched-kernel don't use external module
+ --with-kvm-trace Enable kvm_trace
+ --kerneldir=DIR kernel build directory ($kerneldir)
+ --qemu-cflags=CFLAGS CFLAGS to add to qemu configuration
+ --qemu-ldflags=LDFLAGS LDFLAGS to add to qemu configuration
+
+ Any additional option is given to qemu's configure verbatim; including:
+
+EOF
+ cd qemu
+ ./configure --help | egrep "enable-|disable-" \
+ | grep -v user | grep -v system | grep -v kqemu | grep -v kvm \
+ | sed -e "s/^ / /g" \
+ | sed -e"s/ enable/enable/g" | sed -e "s/ disable/disable/g"
+ exit 1
+}
+
+while [[ "$1" = -* ]]; do
+ opt="$1"; shift
+ arg=
+ hasarg=
+ if [[ "$opt" = *=* ]]; then
+ arg="${opt#*=}"
+ opt="${opt%%=*}"
+ hasarg=1
+ fi
+ case "$opt" in
+ --prefix)
+ prefix="$arg"
+ ;;
+ --kerneldir)
+ kerneldir="$arg"
+ no_uname=1
+ ;;
+ --with-patched-kernel)
+ want_module=
+ ;;
+ --with-kvm-trace)
+ kvm_trace=y
+ ;;
+ --qemu-cflags)
+ qemu_cflags="$arg"
+ ;;
+ --qemu-ldflags)
+ qemu_ldflags="$arg"
+ ;;
+ --arch)
+ arch="$arg"
+ ;;
+ --cross-prefix)
+ cross_prefix="$arg"
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ qemu_opts=("${qemu_opts[@]}" "$opt${hasarg:+=$arg}")
+ ;;
+ esac
+done
+
+
+#set kenel directory
+libkvm_kerneldir=$(readlink -f kernel)
+
+case $arch in
+ i?86*|x86_64*)
+ arch=${arch/#i?86/i386}
+ target_exec="x86_64-softmmu"
+ qemu_cflags="$qemu_cflags -DCONFIG_X86"
+ ;;
+ ia64*)
+ target_exec="ia64-softmmu"
+ ;;
+ powerpc*)
+ target_exec="ppcemb-softmmu"
+ qemu_cflags="$qemu_cflags -I $PWD/libfdt"
+ qemu_ldflags="$qemu_ldflags -L $PWD/libfdt"
+ ;;
+esac
+
+processor=${arch#*-}
+arch=${arch%%-*}
+
+# see if we have split build and source directories
+if [ -d "$kerneldir/include2" ]; then
+ kernelsourcedir=${kerneldir%/*}/source
+fi
+
+if [ -n "$no_uname" ]; then
+ if [ -e "$kerneldir/.kernelrelease" ]; then
+ depmod_version=`cat "$kerneldir/.kernelrelease"`
+
+ elif [ -e "$kerneldir/include/config/kernel.release" ]; then
+ depmod_version=`cat "$kerneldir/include/config/kernel.release"`
+ else
+ echo
+ echo "Error: kernelversion not found"
+ echo "Please make sure your kernel is configured"
+ echo
+ exit 1
+ fi
+fi
+
+#configure user dir
+(cd user; ./configure --prefix="$prefix" --kerneldir="$libkvm_kerneldir" \
+ --arch="$arch" --processor="$processor" \
+ ${cross_prefix:+"--cross-prefix=$cross_prefix"})
+
+#configure qemu
+(cd qemu; ./configure --target-list=$target_exec \
+ --disable-kqemu \
+ --extra-cflags="-I $PWD/../libkvm $qemu_cflags" \
+ --extra-ldflags="-L $PWD/../libkvm $qemu_ldflags" \
+ --kerneldir="$libkvm_kerneldir" \
+ --prefix="$prefix" \
+ ${cross_prefix:+"--cross-prefix=$cross_prefix"} \
+ ${cross_prefix:+"--cpu=$arch"} "${qemu_opts[@]}"
+) || usage
+
+
+cat <<EOF > config.mak
+ARCH=$arch
+PROCESSOR=$processor
+PREFIX=$prefix
+KERNELDIR=$kerneldir
+KERNELSOURCEDIR=$kernelsourcedir
+LIBKVM_KERNELDIR=$libkvm_kerneldir
+WANT_MODULE=$want_module
+CROSS_COMPILE=$cross_prefix
+CC=$cross_prefix$cc
+LD=$cross_prefix$ld
+OBJCOPY=$cross_prefix$objcopy
+AR=$cross_prefix$ar
+DEPMOD_VERSION=$depmod_version
+EOF
+
+cat <<EOF > kernel/config.kbuild
+EXT_CONFIG_KVM_TRACE=$kvm_trace
+EOF
diff --git a/kvm/doxygen.conf b/kvm/doxygen.conf
new file mode 100644
index 000000000..21a04c0a9
--- /dev/null
+++ b/kvm/doxygen.conf
@@ -0,0 +1,1252 @@
+# Doxyfile 1.5.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = KVM
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = Release 7
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = NO
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = user/ kernel/
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = NO
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that a graph may be further truncated if the graph's
+# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH
+# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default),
+# the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/kvm/extboot/Makefile b/kvm/extboot/Makefile
new file mode 100644
index 000000000..ab2dae70d
--- /dev/null
+++ b/kvm/extboot/Makefile
@@ -0,0 +1,41 @@
+OBJCOPY=objcopy
+
+# from kernel sources - scripts/Kbuild.include
+# try-run
+# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
+# Exit code chooses option. "$$TMP" is can be used as temporary file and
+# is automatically cleaned up.
+try-run = $(shell set -e; \
+ TMP="$(TMPOUT).$$$$.tmp"; \
+ if ($(1)) >/dev/null 2>&1; \
+ then echo "$(2)"; \
+ else echo "$(3)"; \
+ fi; \
+ rm -f "$$TMP")
+
+# cc-option-yn
+# Usage: flag := $(call cc-option-yn,-march=winchip-c6)
+cc-option-yn = $(call try-run,\
+ $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",y,n)
+
+CFLAGS = -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin
+ifeq ($(call cc-option-yn,-fno-stack-protector),y)
+CFLAGS += -fno-stack-protector
+endif
+
+all: extboot.bin
+
+%.o: %.S
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+extboot.img: extboot.o
+ $(LD) --oformat binary -Ttext 0 -o $@ $<
+
+extboot.bin: extboot.img signrom
+ ./signrom extboot.img extboot.bin
+
+signrom: signrom.c
+ $(CC) -o $@ -g -Wall $^
+
+clean:
+ $(RM) *.o *.img *.bin signrom *~
diff --git a/kvm/extboot/STATUS b/kvm/extboot/STATUS
new file mode 100644
index 000000000..687c6d64c
--- /dev/null
+++ b/kvm/extboot/STATUS
@@ -0,0 +1,6 @@
+Working
+-------
+
+Ubuntu Server 7.04 (i386)
+Windows 2000 Professional (i386)
+Windows XP SP2 (i386)
diff --git a/kvm/extboot/extboot.S b/kvm/extboot/extboot.S
new file mode 100644
index 000000000..e3d1adf84
--- /dev/null
+++ b/kvm/extboot/extboot.S
@@ -0,0 +1,692 @@
+/*
+ * Extended Boot Option ROM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ * Authors: Anthony Liguori <aliguori@us.ibm.com>
+ */
+
+.code16
+.text
+ .global _start
+_start:
+ .short 0xaa55
+ .byte (_end - _start) / 512
+ push %eax
+ push %ds
+
+ /* setup ds so we can access the IVT */
+ xor %ax, %ax
+ mov %ax, %ds
+
+ /* save old int 19 */
+ mov (0x19*4), %eax
+ mov %eax, %cs:old_int19
+
+ /* install out int 19 handler */
+ movw $int19_handler, (0x19*4)
+ mov %cs, (0x19*4+2)
+
+ pop %ds
+ pop %eax
+ lret
+
+int19_handler:
+ push %eax
+ push %bx
+ push %cx
+ push %dx
+ push %ds
+
+ /* setup ds to access IVT */
+ xor %ax, %ax
+ mov %ax, %ds
+
+ movw $0x404, %dx
+ inb %dx, %al
+ cmp $1, %al
+ je 1f
+ cmp $2, %al
+ je 2f
+ jmp 3f
+
+1: /* hook int13: intb(0x404) == 1 */
+ /* save old int 13 to int 2c */
+ mov (0x13*4), %eax
+ mov %eax, %cs:old_int13
+
+ /* install our int 13 handler */
+ movw $int13_handler, (0x13*4)
+ mov %cs, (0x13*4+2)
+ jmp 3f
+
+2: /* linux boot: intb(0x404) == 2 */
+ cli
+ cld
+ mov $0x9000, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $0x8ffe, %sp
+ ljmp $0x9000 + 0x20, $0
+
+3: /* fall through: inb(0x404) == 0 */
+ /* restore previous int $0x19 handler */
+ mov %cs:old_int19,%eax
+ mov %eax,(0x19*4)
+
+ pop %ds
+ pop %dx
+ pop %cx
+ pop %bx
+ pop %eax
+ ljmpw *%cs:old_int19
+
+#define FLAGS_CF 0x01
+
+/* The two macro below clear/set the carry flag to indicate the status
+ * of the interrupt execution. It is not enough to issue a clc/stc instruction,
+ * since the value of the flags register will be overwritten by whatever is
+ * in the stack frame
+ */
+.macro clc_stack
+ push %bp
+ mov %sp, %bp
+ /* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */
+ and $(~FLAGS_CF), 8(%bp)
+ pop %bp
+.endm
+
+.macro stc_stack
+ push %bp
+ /* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */
+ or $(FLAGS_CF), 8(%bp)
+ pop %bp
+.endm
+
+/* we clobber %bx */
+.macro alloca size
+ push %ds
+ push %bp
+ mov %sp, %bp /* remember the current stack position */
+
+ mov %ss, %bx
+ mov %bx, %ds
+
+ sub \size, %sp
+ and $(~0x0F), %sp
+ mov %sp, %bx
+
+ push %bp
+ mov 0(%bp), %bp
+.endm
+
+/* we clobber %bp */
+.macro allocbpa size
+ mov %sp, %bp /* remember the current stack position */
+ sub \size, %sp
+ and $(~0x0F), %sp
+ push %bp
+ mov %sp, %bp
+ add $2, %bp
+.endm
+
+.macro freea
+ pop %sp
+ add $2, %sp
+ pop %ds
+.endm
+
+.macro freebpa
+ pop %sp
+.endm
+
+.macro dump reg
+ push %ax
+ push %dx
+
+ mov \reg, %ax
+ mov $0x406, %dx
+ outw %ax, %dx
+
+ pop %dx
+ pop %ax
+.endm
+
+.macro callout value
+ push %bp
+ push %bx
+ mov %sp, %bp
+ alloca $16
+ push %ax
+ push %dx
+
+ mov %ax, 0(%bx) /* ax */
+ mov 0(%bp), %ax /* bx */
+ mov %ax, 2(%bx)
+ mov %cx, 4(%bx) /* cx */
+ mov %dx, 6(%bx) /* dx */
+ mov %si, 8(%bx) /* si */
+ mov %ds, 10(%bx) /* ds */
+ mov %es, 12(%bx) /* ds */
+ movw \value, 14(%bx) /* value */
+
+ mov %bx, %ax
+ shr $4, %ax
+ mov %ds, %dx
+ add %dx, %ax
+
+ mov $0x407, %dx
+ outw %ax, %dx
+
+ pop %dx
+ pop %ax
+ freea
+ pop %bx
+ pop %bp
+.endm
+
+send_command:
+ push %bp
+ mov %sp, %bp
+ push %ax
+ push %bx
+ push %dx
+
+ mov 4(%bp), %ax
+ shr $4, %ax
+ and $0x0FFF, %ax
+ mov %ss, %bx
+ add %bx, %ax
+
+ mov $0x405, %dx
+ outw %ax, %dx
+
+ pop %dx
+ pop %bx
+ pop %ax
+ pop %bp
+
+ push %ax
+ mov 2(%bx), %ax
+ pop %ax
+
+ ret
+
+add32: /* lo, hi, lo, hi */
+ push %bp
+ mov %sp, %bp
+
+ movw 4(%bp), %cx /* hi */
+ movw 6(%bp), %dx /* lo */
+
+ add 10(%bp), %dx
+ jnc 1f
+ add $1, %cx
+1: add 8(%bp), %cx
+
+ pop %bp
+ ret
+
+mul32: /* lo, hi, lo, hi */
+ /* 10(%bp), 8(%bp), 6(%bp), 4(%bp) */
+ push %bp
+ mov %sp, %bp
+ push %ax
+ push %bx
+
+ xor %cx, %cx
+ xor %dx, %dx
+
+ /* for (i = 0; i < 16;) */
+ xor %bx, %bx
+0:
+ cmp $16, %bx
+ jge 2f
+
+ mov 6(%bp), %ax
+ and $1, %ax
+ cmp $1, %ax
+ jne 1f
+ push 10(%bp)
+ push 8(%bp)
+ push %dx
+ push %cx
+ call add32
+ add $8, %sp
+1:
+ shlw $1, 8(%bp)
+ movw 10(%bp), %ax
+ and $0x8000, %ax
+ cmp $0x8000, %ax
+ jne 1f
+ orw $1, 8(%bp)
+1:
+ shlw $1, 10(%bp)
+ shrw $1, 6(%bp)
+
+ /* i++) { */
+ add $1, %bx
+ jmp 0b
+
+2:
+ pop %bx
+ pop %ax
+ pop %bp
+ ret
+
+disk_reset:
+ movb $0, %ah
+ clc_stack
+ ret
+
+/* this really should be a function, not a macro but i'm lazy */
+.macro read_write_disk_sectors cmd
+ push %ax
+ push %bx
+ push %cx
+ push %dx
+ push %si
+
+ push %bp
+ sub $10, %sp
+ mov %sp, %bp
+
+ /* save nb_sectors */
+ mov %al, 6(%bp)
+ movb $0, 7(%bp)
+
+ /* save buffer */
+ mov %bx, 8(%bp)
+
+ /* cylinders */
+ xor %ax, %ax
+ mov %cl, %al
+ shl $2, %ax
+ and $0x300, %ax
+ mov %ch, %al
+ mov %ax, 0(%bp)
+
+ /* heads */
+ xor %ax, %ax
+ mov %dh, %al
+ mov %ax, 2(%bp)
+
+ /* sectors - 1 */
+ xor %ax, %ax
+ mov %cl, %al
+ and $0x3F, %al
+ sub $1, %ax
+ mov %ax, 4(%bp)
+
+ alloca $16
+
+ movw $0, 0(%bx) /* read c,h,s */
+ push %bx
+ call send_command
+ add $2, %sp
+
+ mov 6(%bx), %ax /* total_sectors */
+ mov 2(%bp), %si /* *= heads */
+ mul %si
+ add 4(%bp), %ax /* += sectors - 1 */
+
+ push 4(%bx) /* total_heads */
+ push $0
+ push 6(%bx) /* total_sectors */
+ push $0
+ call mul32
+ add $8, %sp
+
+ push 0(%bp) /* cylinders */
+ push $0
+ push %dx
+ push %cx
+ call mul32
+ add $8, %sp
+
+ add %ax, %dx
+ jnc 1f
+ add $1, %cx
+1:
+ freea
+
+ alloca $16
+
+ movw \cmd, 0(%bx) /* read */
+ movw 6(%bp), %ax /* nb_sectors */
+ movw %ax, 2(%bx)
+ movw %es, 4(%bx) /* segment */
+ movw 8(%bp), %ax /* offset */
+ mov %ax, 6(%bx)
+ movw %dx, 8(%bx) /* sector */
+ movw %cx, 10(%bx)
+ movw $0, 12(%bx)
+ movw $0, 14(%bx)
+
+ push %bx
+ call send_command
+ add $2, %sp
+
+ freea
+
+ add $10, %sp
+ pop %bp
+
+ pop %si
+ pop %dx
+ pop %cx
+ pop %bx
+ pop %ax
+
+ mov $0, %ah
+ clc_stack
+ ret
+.endm
+
+read_disk_sectors:
+ read_write_disk_sectors $0x01
+
+write_disk_sectors:
+ read_write_disk_sectors $0x02
+
+read_disk_drive_parameters:
+ push %bx
+
+ /* allocate memory for packet, pointer gets returned in bx */
+ alloca $16
+
+ /* issue command */
+ movw $0, 0(%bx) /* cmd = 0, read c,h,s */
+ push %bx
+ call send_command
+ add $2, %sp
+
+ /* normalize sector value */
+ movb 6(%bx), %cl
+ andb $0x3F, %cl
+ movb %cl, 6(%bx)
+
+ /* normalize cylinders */
+ subw $2, 2(%bx)
+
+ /* normalize heads */
+ subw $1, 4(%bx)
+
+ /* return code */
+ mov $0, %ah
+
+ /* cylinders */
+ movb 2(%bx), %ch
+ movb 3(%bx), %cl
+ shlb $6, %cl
+ andb $0xC0, %cl
+
+ /* sectors */
+ orb 6(%bx), %cl
+
+ /* heads */
+ movb 4(%bx), %dh
+
+ /* drives */
+ movb $1, %dl
+
+ /* status */
+ mov $0, %ah
+
+ freea
+
+ pop %bx
+
+ /* do this last since it's the most sensitive */
+ clc_stack
+ ret
+
+alternate_disk_reset:
+ movb $0, %ah
+ clc_stack
+ ret
+
+read_disk_drive_size:
+ push %bx
+ alloca $16
+
+ movw $0, 0(%bx) /* cmd = 0, read c,h,s */
+ push %bx
+ call send_command
+ add $2, %sp
+
+ /* cylinders - 1 to cx:dx */
+ mov 2(%bx), %dx
+ xor %cx, %cx
+ sub $1, %dx
+
+ /* heads */
+ push 4(%bx)
+ push $0
+ push %dx
+ push %cx
+ call mul32
+ add $8, %sp
+
+ /* sectors */
+ push 6(%bx)
+ push $0
+ push %dx
+ push %cx
+ call mul32
+ add $8, %sp
+
+ /* status */
+ mov $3, %ah
+
+ freea
+ pop %bx
+
+ clc_stack
+ ret
+
+check_if_extensions_present:
+ mov $0x30, %ah
+ mov $0xAA55, %bx
+ mov $0x07, %cx
+ clc_stack
+ ret
+
+.macro extended_read_write_sectors cmd
+ cmpb $10, 0(%si)
+ jg 1f
+ mov $1, %ah
+ stc_stack
+ ret
+1:
+ push %ax
+ push %bp
+ allocbpa $16
+
+ movw \cmd, 0(%bp) /* read */
+ movw 2(%si), %ax /* nb_sectors */
+ movw %ax, 2(%bp)
+ movw 4(%si), %ax /* offset */
+ movw %ax, 6(%bp)
+ movw 6(%si), %ax /* segment */
+ movw %ax, 4(%bp)
+ movw 8(%si), %ax /* block */
+ movw %ax, 8(%bp)
+ movw 10(%si), %ax
+ movw %ax, 10(%bp)
+ movw 12(%si), %ax
+ movw %ax, 12(%bp)
+ movw 14(%si), %ax
+ movw %ax, 14(%bp)
+
+ push %bp
+ call send_command
+ add $2, %sp
+
+ freebpa
+ pop %bp
+ pop %ax
+
+ mov $0, %ah
+ clc_stack
+ ret
+.endm
+
+extended_read_sectors:
+ extended_read_write_sectors $0x01
+
+extended_write_sectors:
+ extended_read_write_sectors $0x02
+
+get_extended_drive_parameters:
+ push %ax
+ push %bp
+ push %cx
+ push %dx
+
+ allocbpa $16
+
+ movw $0, 0(%bp) /* read c,h,s */
+ push %bp
+ call send_command
+ add $2, %sp
+
+ /* write size */
+ movw $26, 0(%si)
+
+ /* set flags to 2 */
+ movw $2, 2(%si)
+
+ /* cylinders */
+ mov 2(%bp), %ax
+ mov %ax, 4(%si)
+ xor %ax, %ax
+ mov %ax, 6(%si)
+
+ /* heads */
+ mov 4(%bp), %ax
+ mov %ax, 8(%si)
+ xor %ax, %ax
+ mov %ax, 10(%si)
+
+ /* sectors */
+ mov 6(%bp), %ax
+ mov %ax, 12(%si)
+ xor %ax, %ax
+ mov %ax, 14(%si)
+
+ /* set total number of sectors */
+ mov 8(%bp), %ax
+ mov %ax, 16(%si)
+ mov 10(%bp), %ax
+ mov %ax, 18(%si)
+ mov 12(%bp), %ax
+ mov %ax, 20(%si)
+ mov 14(%bp), %ax
+ mov %ax, 22(%si)
+
+ /* number of bytes per sector */
+ movw $512, 24(%si)
+
+ freebpa
+
+ pop %dx
+ pop %cx
+ pop %bp
+ pop %ax
+
+ mov $0, %ah
+ clc_stack
+ ret
+
+terminate_disk_emulation:
+ mov $1, %ah
+ stc_stack
+ ret
+
+int13_handler:
+ cmp $0x80, %dl
+ je 1f
+ ljmpw *%cs:old_int13
+1:
+ cmp $0x0, %ah
+ jne 1f
+ call disk_reset
+ iret
+1:
+ cmp $0x2, %ah
+ jne 1f
+ call read_disk_sectors
+ iret
+1:
+ cmp $0x8, %ah
+ jne 1f
+ call read_disk_drive_parameters
+ iret
+1:
+ cmp $0x15, %ah
+ jne 1f
+ call read_disk_drive_size
+ iret
+1:
+ cmp $0x41, %ah
+ jne 1f
+ call check_if_extensions_present
+ iret
+1:
+ cmp $0x42, %ah
+ jne 1f
+ call extended_read_sectors
+ iret
+1:
+ cmp $0x48, %ah
+ jne 1f
+ call get_extended_drive_parameters
+ iret
+1:
+ cmp $0x4b, %ah
+ jne 1f
+ call terminate_disk_emulation
+ iret
+1:
+ cmp $0x0d, %ah
+ jne 1f
+ call alternate_disk_reset
+ iret
+1:
+ cmp $0x03, %ah
+ jne 1f
+ call write_disk_sectors
+ iret
+1:
+ cmp $0x43, %ah
+ jne 1f
+ call extended_write_sectors
+ iret
+1:
+ int $0x18 /* boot failed */
+ iret
+
+/* Variables */
+.align 4, 0
+old_int13: .long 0
+old_int19: .long 0
+
+.align 512, 0
+_end:
diff --git a/kvm/extboot/signrom.c b/kvm/extboot/signrom.c
new file mode 100644
index 000000000..fe8d67745
--- /dev/null
+++ b/kvm/extboot/signrom.c
@@ -0,0 +1,79 @@
+/*
+ * Extended Boot Option ROM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ * Authors: Anthony Liguori <aliguori@us.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ FILE *fin, *fout;
+ char buffer[512], oldbuffer[512];
+ int i, size, lag = 0;
+ uint8_t sum = 0;
+
+ if (argc != 3) {
+ printf("Usage: %s ROM OUTPUT\n", argv[0]);
+ return 1;
+ }
+
+ fin = fopen(argv[1], "rb");
+ fout = fopen(argv[2], "wb");
+
+ if (fin == NULL || fout == NULL) {
+ fprintf(stderr, "Could not open input/output files\n");
+ return 1;
+ }
+
+ do {
+ size = fread(buffer, 512, 1, fin);
+ if (size == 1) {
+ for (i = 0; i < 512; i++)
+ sum += buffer[i];
+
+ if (lag) {
+ if (fwrite(oldbuffer, 512, 1, fout) != 1) {
+ fprintf(stderr, "Write failed\n");
+ return 1;
+ }
+ }
+ lag = 1;
+ memcpy(oldbuffer, buffer, 512);
+ }
+ } while (size == 1);
+
+ if (size != 0) {
+ fprintf(stderr, "Failed to read from input file\n");
+ return 1;
+ }
+
+ oldbuffer[511] = -sum;
+
+ if (fwrite(oldbuffer, 512, 1, fout) != 1) {
+ fprintf(stderr, "Failed to write to output file\n");
+ return 1;
+ }
+
+ fclose(fin);
+ fclose(fout);
+
+ return 0;
+}
diff --git a/kvm/kernel/Kbuild b/kvm/kernel/Kbuild
new file mode 100644
index 000000000..ec34c43e7
--- /dev/null
+++ b/kvm/kernel/Kbuild
@@ -0,0 +1,2 @@
+obj-$(CONFIG_X86) += x86/
+obj-$(CONFIG_IA64) += ia64/
diff --git a/kvm/kernel/Makefile b/kvm/kernel/Makefile
new file mode 100644
index 000000000..8315e3dbd
--- /dev/null
+++ b/kvm/kernel/Makefile
@@ -0,0 +1,129 @@
+include ../config.mak
+include config.kbuild
+
+ARCH_DIR = $(if $(filter $(ARCH),x86_64 i386),x86,$(ARCH))
+ARCH_CONFIG := $(shell echo $(ARCH_DIR) | tr '[:lower:]' '[:upper:]')
+# NONARCH_CONFIG used for unifdef, and only cover X86 and IA64 now
+NONARCH_CONFIG = $(filter-out $(ARCH_CONFIG),X86 IA64)
+
+KVERREL = $(patsubst /lib/modules/%/build,%,$(KERNELDIR))
+
+DESTDIR=
+
+MAKEFILE_PRE = $(ARCH_DIR)/Makefile.pre
+
+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 && \
+ gawk -v version=$(version) -f $(ARCH_DIR)/hack-module.awk $1.orig \
+ | sed '/\#include/! s/\blapic\b/l_apic/g' > $1 && rm $1.orig
+
+unifdef = mv $1 $1.orig && cat unifdef.h $1.orig > $1 && rm $1.orig
+
+hack = $(call _hack,$T/$(strip $1))
+
+hack-files-x86 = kvm_main.c mmu.c vmx.c svm.c x86.c irq.h lapic.c i8254.c kvm_trace.c
+hack-files-ia64 = kvm_main.c kvm_fw.c kvm_lib.c
+
+hack-files = $(hack-files-$(ARCH_DIR))
+
+ifeq ($(EXT_CONFIG_KVM_TRACE),y)
+module_defines += -DEXT_CONFIG_KVM_TRACE=y
+endif
+
+all:: header-link prerequisite
+# include header priority 1) $LINUX 2) $KERNELDIR 3) include-compat
+ $(MAKE) -C $(KERNELDIR) M=`pwd` \
+ LINUXINCLUDE="-I`pwd`/include -Iinclude \
+ $(if $(KERNELSOURCEDIR),-Iinclude2 -I$(KERNELSOURCEDIR)/include) \
+ -Iarch/${ARCH_DIR}/include -I`pwd`/include-compat \
+ -include include/linux/autoconf.h \
+ -include `pwd`/$(ARCH_DIR)/external-module-compat.h $(module_defines)"
+ "$$@"
+
+sync: header-sync source-sync header-link
+
+header-link:
+ rm -f include/asm include-compat/asm
+ ln -sf asm-$(ARCH_DIR) include/asm
+ ln -sf asm-$(ARCH_DIR) include-compat/asm
+
+T = $(subst -sync,,$@)-tmp
+
+headers-old = $(LINUX)/./include/asm-$(ARCH_DIR)/kvm*.h
+headers-new = $(LINUX)/arch/$(ARCH_DIR)/include/asm/./kvm*.h \
+ $(LINUX)/arch/$(ARCH_DIR)/include/asm/./vmx*.h \
+ $(LINUX)/arch/$(ARCH_DIR)/include/asm/./svm*.h \
+ $(LINUX)/arch/$(ARCH_DIR)/include/asm/./virtext*.h
+
+header-sync:
+ rm -rf $T
+ rsync -R \
+ "$(LINUX)"/./include/linux/kvm*.h \
+ $(if $(wildcard $(headers-old)), $(headers-old)) \
+ $T/
+ $(if $(wildcard $(headers-new)), \
+ rsync -R \
+ $(wildcard $(headers-new)) \
+ $T/include/asm-$(ARCH_DIR)/)
+
+ for i in $$(find $T -name '*.h'); do \
+ $(call unifdef,$$i); done
+ $(call hack, include/linux/kvm.h)
+ set -e && for i in $$(find $T -type f -printf '%P '); \
+ do mkdir -p $$(dirname $$i); cmp -s $$i $T/$$i || cp $T/$$i $$i; done
+ rm -rf $T
+
+source-sync:
+ rm -rf $T
+ rsync --exclude='*.mod.c' -R \
+ "$(LINUX)"/arch/$(ARCH_DIR)/kvm/./*.[cSh] \
+ "$(LINUX)"/virt/kvm/./*.[cSh] \
+ $T/
+
+ for i in $$(find $T -name '*.c'); do \
+ $(call unifdef,$$i); done
+
+ for i in $(hack-files); \
+ do $(call hack, $$i); done
+
+ for i in $$(find $T -type f -printf '%P '); \
+ do cmp -s $(ARCH_DIR)/$$i $T/$$i || cp $T/$$i $(ARCH_DIR)/$$i; done
+ rm -rf $T
+
+include $(MAKEFILE_PRE)
+
+install:
+ mkdir -p $(DESTDIR)/$(INSTALLDIR)
+ cp $(ARCH_DIR)/*.ko $(DESTDIR)/$(INSTALLDIR)
+ for i in $(ORIGMODDIR)/drivers/kvm/*.ko \
+ $(ORIGMODDIR)/arch/$(ARCH_DIR)/kvm/*.ko; do \
+ if [ -f "$$i" ]; then mv "$$i" "$$i.orig"; fi; \
+ done
+ /sbin/depmod -a $(DEPMOD_VERSION)
+
+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)/$(ARCH_DIR)" \
+ --define="_rpmdir $(RPMDIR)" \
+ --define="_topdir $(rpm-topdir)" \
+ -bb $(tmpspec)
+
+clean:
+ $(MAKE) -C $(KERNELDIR) M=`pwd` $@
diff --git a/kvm/kernel/anon_inodes.c b/kvm/kernel/anon_inodes.c
new file mode 100644
index 000000000..510303f02
--- /dev/null
+++ b/kvm/kernel/anon_inodes.c
@@ -0,0 +1,268 @@
+/*
+ * 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>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+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
+ *
+ * @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. Returns new descriptor or -error.
+ */
+int anon_inode_getfd(const char *name, const struct file_operations *fops,
+ void *priv, int flags)
+{
+ 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);
+
+ return fd;
+
+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);
+}
+
+#else
+
+int kvm_init_anon_inodes(void)
+{
+ return 0;
+}
+
+void kvm_exit_anon_inodes(void)
+{
+}
+
+#undef anon_inode_getfd
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+
+int kvm_anon_inode_getfd(const char *name,
+ const struct file_operations *fops,
+ void *priv, int flags)
+{
+ int r;
+ int fd;
+ struct inode *inode;
+ struct file *file;
+
+ r = anon_inode_getfd(&fd, &inode, &file, name, fops, priv);
+ if (r < 0)
+ return r;
+ return fd;
+}
+
+#elif LINUX_VERSION_CODE == KERNEL_VERSION(2,6,26)
+
+int kvm_anon_inode_getfd(const char *name,
+ const struct file_operations *fops,
+ void *priv, int flags)
+{
+ return anon_inode_getfd(name, fops, priv);
+}
+
+#else
+
+int kvm_anon_inode_getfd(const char *name,
+ const struct file_operations *fops,
+ void *priv, int flags)
+{
+ return anon_inode_getfd(name, fops, priv, flags);
+}
+
+#endif
+
+#endif
diff --git a/kvm/kernel/external-module-compat-comm.h b/kvm/kernel/external-module-compat-comm.h
new file mode 100644
index 000000000..27fea1553
--- /dev/null
+++ b/kvm/kernel/external-module-compat-comm.h
@@ -0,0 +1,694 @@
+
+/*
+ * 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 <linux/string.h>
+#include <linux/kvm.h>
+#include <linux/kvm_para.h>
+#include <linux/cpu.h>
+#include <linux/time.h>
+#include <asm/processor.h>
+#include <linux/hrtimer.h>
+#include <asm/bitops.h>
+
+/* Override CONFIG_KVM_TRACE */
+#ifdef EXT_CONFIG_KVM_TRACE
+# define CONFIG_KVM_TRACE 1
+#else
+# undef CONFIG_KVM_TRACE
+#endif
+
+/*
+ * 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, and has different
+ * semantics below 2.6.23. The 'nonatomic' argument was removed in 2.6.27.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+int kvm_smp_call_function_single(int cpu, void (*func)(void *info),
+ void *info, int wait);
+
+#define smp_call_function_single kvm_smp_call_function_single
+
+#endif
+
+/* on_each_cpu() lost an argument in 2.6.27. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+#define kvm_on_each_cpu(func, info, wait) on_each_cpu(func, info, 0, wait)
+
+#else
+
+#define kvm_on_each_cpu(func, info, wait) on_each_cpu(func, info, wait)
+
+#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
+
+#include <asm/system.h>
+
+struct inode;
+#include <linux/anon_inodes.h>
+#define anon_inode_getfd kvm_anon_inode_getfd
+int kvm_init_anon_inodes(void);
+void kvm_exit_anon_inodes(void);
+int anon_inode_getfd(const char *name,
+ const struct file_operations *fops,
+ void *priv , int flags);
+
+/*
+ * 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
+
+/* 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_u64 is fairly new */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+
+#define div64_u64 kvm_div64_u64
+
+#ifdef CONFIG_64BIT
+
+static inline uint64_t div64_u64(uint64_t dividend, uint64_t divisor)
+{
+ return dividend / divisor;
+}
+
+#else
+
+uint64_t div64_u64(uint64_t dividend, uint64_t divisor);
+
+#endif
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+
+#ifdef RHEL_RELEASE_CODE
+#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(5,2)
+#define RHEL_BOOL 1
+#endif
+#endif
+
+#ifndef RHEL_BOOL
+
+typedef _Bool bool;
+
+#endif
+
+#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)
+
+int kvm_smp_call_function_mask(cpumask_t mask, void (*func) (void *info),
+ void *info, int wait);
+
+#define smp_call_function_mask kvm_smp_call_function_mask
+
+#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)
+#define mmget(x) do { (void)(x); } while (0)
+
+#else
+
+#define mmget(x) do { atomic_inc(x); } while (0)
+
+#endif
+
+/* pagefault_enable(), page_fault_disable() - 2.6.20 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+static inline void pagefault_disable(void)
+{
+ inc_preempt_count();
+ /*
+ * make sure to have issued the store before a pagefault
+ * can hit.
+ */
+ barrier();
+}
+
+static inline void pagefault_enable(void)
+{
+ /*
+ * make sure to issue those last loads/stores before enabling
+ * the pagefault handler again.
+ */
+ barrier();
+ dec_preempt_count();
+ /*
+ * make sure we do..
+ */
+ barrier();
+ preempt_check_resched();
+}
+
+#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
+
+/* simple vfs attribute getter signature has changed to add a return code */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+
+#define MAKE_SIMPLE_ATTRIBUTE_GETTER(x) \
+ static u64 x(void *v) \
+ { \
+ u64 ret = 0; \
+ \
+ __##x(v, &ret); \
+ return ret; \
+ }
+
+#else
+
+#define MAKE_SIMPLE_ATTRIBUTE_GETTER(x) \
+ static int x(void *v, u64 *val) \
+ { \
+ return __##x(v, val); \
+ }
+
+#endif
+
+/* set_kset_name() is gone in 2.6.25 */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
+
+#define set_kset_name(x) .name = x
+
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
+#ifndef FASTCALL
+#define FASTCALL(x) x
+#define fastcall
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+static unsigned __attribute__((__used__)) kvm_tsc_khz = 2000000;
+
+#else
+
+#define kvm_tsc_khz tsc_khz
+
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21)
+
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+
+#define ktime_get kvm_ktime_get
+
+static inline ktime_t ktime_get(void)
+{
+ struct timespec now;
+
+ ktime_get_ts(&now);
+
+ return timespec_to_ktime(now);
+}
+
+#endif
+
+/* __aligned arrived in 2.6.21 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define __aligned(x) __attribute__((__aligned__(x)))
+#endif
+
+#include <linux/mm.h>
+
+/* The shrinker API changed in 2.6.23 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+struct kvm_shrinker {
+ int (*shrink)(int nr_to_scan, gfp_t gfp_mask);
+ int seeks;
+ struct shrinker *kshrinker;
+};
+
+static inline void register_shrinker(struct kvm_shrinker *shrinker)
+{
+ shrinker->kshrinker = set_shrinker(shrinker->seeks, shrinker->shrink);
+}
+
+static inline void unregister_shrinker(struct kvm_shrinker *shrinker)
+{
+ if (shrinker->kshrinker)
+ remove_shrinker(shrinker->kshrinker);
+}
+
+#define shrinker kvm_shrinker
+
+#endif
+
+/* clocksource */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+static inline u32 clocksource_khz2mult(u32 khz, u32 shift_constant)
+{
+ /* khz = cyc/(Million ns)
+ * mult/2^shift = ns/cyc
+ * mult = ns/cyc * 2^shift
+ * mult = 1Million/khz * 2^shift
+ * mult = 1000000 * 2^shift / khz
+ * mult = (1000000<<shift) / khz
+ */
+ u64 tmp = ((u64)1000000) << shift_constant;
+
+ tmp += khz/2; /* round for do_div */
+ do_div(tmp, khz);
+
+ return (u32)tmp;
+}
+#else
+#include <linux/clocksource.h>
+#endif
+
+/* manually export hrtimer_init/start/cancel */
+#include <linux/kallsyms.h>
+extern void (*hrtimer_init_p)(struct hrtimer *timer, clockid_t which_clock,
+ enum hrtimer_mode mode);
+extern int (*hrtimer_start_p)(struct hrtimer *timer, ktime_t tim,
+ const enum hrtimer_mode mode);
+extern int (*hrtimer_cancel_p)(struct hrtimer *timer);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) && defined(CONFIG_KALLSYMS)
+static inline void hrtimer_kallsyms_resolve(void)
+{
+ hrtimer_init_p = (void *) kallsyms_lookup_name("hrtimer_init");
+ BUG_ON(!hrtimer_init_p);
+ hrtimer_start_p = (void *) kallsyms_lookup_name("hrtimer_start");
+ BUG_ON(!hrtimer_start_p);
+ hrtimer_cancel_p = (void *) kallsyms_lookup_name("hrtimer_cancel");
+ BUG_ON(!hrtimer_cancel_p);
+}
+#else
+static inline void hrtimer_kallsyms_resolve(void)
+{
+ hrtimer_init_p = hrtimer_init;
+ hrtimer_start_p = hrtimer_start;
+ hrtimer_cancel_p = hrtimer_cancel;
+}
+#endif
+
+/* handle old hrtimer API with data pointer */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
+static inline void hrtimer_data_pointer(struct hrtimer *timer)
+{
+ timer->data = (void *)timer;
+}
+#else
+static inline void hrtimer_data_pointer(struct hrtimer *timer) {}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+
+#define ns_to_timespec kvm_ns_to_timespec
+
+struct timespec kvm_ns_to_timespec(const s64 nsec);
+
+#endif
+
+/* work_struct lost the 'data' field in 2.6.20 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+#define kvm_INIT_WORK(work, handler) \
+ INIT_WORK(work, (void (*)(void *))handler, work)
+
+#else
+
+#define kvm_INIT_WORK(work, handler) INIT_WORK(work, handler)
+
+#endif
+
+/* cancel_work_sync() was flush_work() in 2.6.21 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+
+static inline int cancel_work_sync(struct work_struct *work)
+{
+ /*
+ * FIXME: actually cancel. How? Add own implementation of workqueues?
+ */
+ return 0;
+}
+
+/* ... and it returned void before 2.6.23 */
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+
+#define cancel_work_sync(work) ({ cancel_work_sync(work); 0; })
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+struct pci_dev;
+
+struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn);
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+
+#include <linux/relay.h>
+
+/* relay_open() interface has changed on 2.6.21 */
+
+struct rchan *kvm_relay_open(const char *base_filename,
+ struct dentry *parent,
+ size_t subbuf_size,
+ size_t n_subbufs,
+ struct rchan_callbacks *cb,
+ void *private_data);
+
+#else
+
+#define kvm_relay_open relay_open
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+static inline int get_user_pages_fast(unsigned long start, int nr_pages,
+ int write, struct page **pages)
+{
+ int npages;
+
+ down_read(&current->mm->mmap_sem);
+ npages = get_user_pages(current, current->mm, start, nr_pages, write,
+ 0, pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ return npages;
+}
+
+#endif
+
+/* spin_needbreak() was called something else in 2.6.24 */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24)
+
+#define spin_needbreak need_lockbreak
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+static inline void hrtimer_add_expires_ns(struct hrtimer *timer, u64 delta)
+{
+ timer->expires = ktime_add_ns(timer->expires, delta);
+}
+
+static inline ktime_t hrtimer_get_expires(struct hrtimer *timer)
+{
+ return timer->expires;
+}
+
+static inline u64 hrtimer_get_expires_ns(struct hrtimer *timer)
+{
+ return ktime_to_ns(timer->expires);
+}
+
+static inline void hrtimer_start_expires(struct hrtimer *timer, int mode)
+{
+ hrtimer_start_p(timer, timer->expires, mode);
+}
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+static inline int pci_reset_function(struct pci_dev *dev)
+{
+ return 0;
+}
+
+#endif
+
+#include <linux/interrupt.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+typedef irqreturn_t (*kvm_irq_handler_t)(int, void *, struct pt_regs *);
+static inline int kvm_request_irq(unsigned int a, kvm_irq_handler_t handler,
+ unsigned long c, const char *d, void *e)
+{
+ /* FIXME: allocate thunk, etc. */
+ return -EINVAL;
+}
+
+#else
+
+#define kvm_request_irq request_irq
+
+#endif
+
+/* dynamically allocated cpu masks introduced in 2.6.28 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+typedef cpumask_t cpumask_var_t[1];
+
+static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
+{
+ return 1;
+}
+
+static inline void free_cpumask_var(cpumask_var_t mask)
+{
+}
+
+static inline void cpumask_clear(cpumask_var_t mask)
+{
+ cpus_clear(*mask);
+}
+
+static inline void cpumask_set_cpu(int cpu, cpumask_var_t mask)
+{
+ cpu_set(cpu, *mask);
+}
+
+static inline int smp_call_function_many(cpumask_var_t cpus,
+ void (*func)(void *data), void *data,
+ int sync)
+{
+ return smp_call_function_mask(*cpus, func, data, sync);
+}
+
+static inline int cpumask_empty(cpumask_var_t mask)
+{
+ return cpus_empty(*mask);
+}
+
+static inline int cpumask_test_cpu(int cpu, cpumask_var_t mask)
+{
+ return cpu_isset(cpu, *mask);
+}
+
+static inline void cpumask_clear_cpu(int cpu, cpumask_var_t mask)
+{
+ cpu_clear(cpu, *mask);
+}
+
+#define cpu_online_mask (&cpu_online_map)
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+
+#define IF_ANON_INODES_DOES_REFCOUNTS(x)
+
+#else
+
+#define IF_ANON_INODES_DOES_REFCOUNTS(x) x
+
+#endif
+
diff --git a/kvm/kernel/external-module-compat.c b/kvm/kernel/external-module-compat.c
new file mode 100644
index 000000000..c0a035525
--- /dev/null
+++ b/kvm/kernel/external-module-compat.c
@@ -0,0 +1,300 @@
+
+/*
+ * smp_call_function_single() is not exported below 2.6.20.
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+#undef smp_call_function_single
+
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+
+struct scfs_thunk_info {
+ int cpu;
+ void (*func)(void *info);
+ void *info;
+};
+
+static void scfs_thunk(void *_thunk)
+{
+ struct scfs_thunk_info *thunk = _thunk;
+
+ if (raw_smp_processor_id() == thunk->cpu)
+ thunk->func(thunk->info);
+}
+
+int kvm_smp_call_function_single(int cpu, void (*func)(void *info),
+ void *info, int wait)
+{
+ int r, this_cpu;
+ struct scfs_thunk_info thunk;
+
+ this_cpu = get_cpu();
+ WARN_ON(irqs_disabled());
+ 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 kvm_smp_call_function_single
+
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+/*
+ * pre 2.6.23 doesn't handle smp_call_function_single on current cpu
+ */
+
+#undef smp_call_function_single
+
+#include <linux/smp.h>
+
+int kvm_smp_call_function_single(int cpu, void (*func)(void *info),
+ void *info, int wait)
+{
+ int this_cpu, r;
+
+ this_cpu = get_cpu();
+ WARN_ON(irqs_disabled());
+ if (cpu == this_cpu) {
+ r = 0;
+ local_irq_disable();
+ func(info);
+ local_irq_enable();
+ } else
+ r = smp_call_function_single(cpu, func, info, 0, wait);
+ put_cpu();
+ return r;
+}
+
+#define smp_call_function_single kvm_smp_call_function_single
+
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+/* The 'nonatomic' argument was removed in 2.6.27. */
+
+#undef smp_call_function_single
+
+#include <linux/smp.h>
+
+int kvm_smp_call_function_single(int cpu, void (*func)(void *info),
+ void *info, int wait)
+{
+ return smp_call_function_single(cpu, func, info, 0, wait);
+}
+
+#define smp_call_function_single kvm_smp_call_function_single
+
+#endif
+
+/* div64_u64 is fairly new */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+
+#ifndef CONFIG_64BIT
+
+/* 64bit divisor, dividend and result. dynamic precision */
+uint64_t div64_u64(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
+
+/*
+ * 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);
+ }
+}
+
+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();
+ WARN_ON(irqs_disabled());
+ 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, 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;
+}
+
+#endif
+
+/* manually export hrtimer_init/start/cancel */
+void (*hrtimer_init_p)(struct hrtimer *timer, clockid_t which_clock,
+ enum hrtimer_mode mode);
+int (*hrtimer_start_p)(struct hrtimer *timer, ktime_t tim,
+ const enum hrtimer_mode mode);
+int (*hrtimer_cancel_p)(struct hrtimer *timer);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+
+static void kvm_set_normalized_timespec(struct timespec *ts, time_t sec,
+ long nsec)
+{
+ while (nsec >= NSEC_PER_SEC) {
+ nsec -= NSEC_PER_SEC;
+ ++sec;
+ }
+ while (nsec < 0) {
+ nsec += NSEC_PER_SEC;
+ --sec;
+ }
+ ts->tv_sec = sec;
+ ts->tv_nsec = nsec;
+}
+
+struct timespec kvm_ns_to_timespec(const s64 nsec)
+{
+ struct timespec ts;
+
+ if (!nsec)
+ return (struct timespec) {0, 0};
+
+ ts.tv_sec = div_long_long_rem_signed(nsec, NSEC_PER_SEC, &ts.tv_nsec);
+ if (unlikely(nsec < 0))
+ kvm_set_normalized_timespec(&ts, ts.tv_sec, ts.tv_nsec);
+
+ return ts;
+}
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+
+#include <linux/pci.h>
+
+struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
+{
+ struct pci_dev *dev = NULL;
+
+ while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ if (pci_domain_nr(dev->bus) == 0 &&
+ (dev->bus->number == bus && dev->devfn == devfn))
+ return dev;
+ }
+ return NULL;
+}
+
+#endif
+
+#include <linux/intel-iommu.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+int intel_iommu_found()
+{
+ return 0;
+}
+
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+
+/* relay_open() interface has changed on 2.6.21 */
+
+struct rchan *kvm_relay_open(const char *base_filename,
+ struct dentry *parent,
+ size_t subbuf_size,
+ size_t n_subbufs,
+ struct rchan_callbacks *cb,
+ void *private_data)
+{
+ struct rchan *chan = relay_open(base_filename, parent,
+ subbuf_size, n_subbufs,
+ cb);
+ if (chan)
+ chan->private_data = private_data;
+ return chan;
+}
+
+#endif
diff --git a/kvm/kernel/ia64/Kbuild b/kvm/kernel/ia64/Kbuild
new file mode 100644
index 000000000..130ec45ef
--- /dev/null
+++ b/kvm/kernel/ia64/Kbuild
@@ -0,0 +1,12 @@
+obj-m := kvm.o kvm-intel.o
+
+kvm-objs := kvm_main.o ioapic.o coalesced_mmio.o kvm-ia64.o kvm_fw.o \
+ irq_comm.o ../anon_inodes.o ../external-module-compat.o
+
+ifeq ($(CONFIG_DMAR),y)
+kvm-objs += vtd.o
+endif
+
+EXTRA_CFLAGS_vcpu.o += -mfixed-range=f2-f5,f12-f127
+kvm-intel-objs := vmm.o vmm_ivt.o trampoline.o vcpu.o optvfault.o mmio.o \
+ vtlb.o process.o memset.o memcpy.o kvm_lib.o
diff --git a/kvm/kernel/ia64/Makefile.pre b/kvm/kernel/ia64/Makefile.pre
new file mode 100644
index 000000000..4d3410f6d
--- /dev/null
+++ b/kvm/kernel/ia64/Makefile.pre
@@ -0,0 +1,27 @@
+prerequisite: asm-offsets.h ia64/memset.S ia64/memcpy.S
+ cp -f $(KERNELDIR)/arch/ia64/lib/memcpy.S ia64/memcpy.S
+ cp -f $(KERNELDIR)/arch/ia64/lib/memset.S ia64/memset.S
+ cmp -s asm-offset.h ia64/asm-offset.h || mv -f asm-offsets.* ia64/
+ cp -f $(KERNELDIR)/lib/vsprintf.c ia64/vsprintf.c
+ cp -f $(KERNELDIR)/lib/ctype.c ia64/ctype.c
+ sed -i /^EXPORT_SYMBOL/d ia64/vsprintf.c
+ sed -i /^EXPORT_SYMBOL/d ia64/ctype.c
+
+asm-offsets.h: asm-offsets.s
+ @(set -e; \
+ echo "/*"; \
+ echo " * DO NOT MODIFY."; \
+ echo " *"; \
+ echo " * This file was auto-generated from $<"; \
+ echo " *"; \
+ echo " */"; \
+ echo ""; \
+ echo "#ifndef __KVM_ASM_OFFSETS_H__"; \
+ echo "#define __KVM_ASM_OFFSETS_H__"; \
+ echo ""; \
+ sed -ne "/^->/{s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}"; \
+ echo ""; \
+ echo "#endif") <$< >$@
+
+asm-offsets.s: ia64/asm-offsets.c
+ gcc -S -D__KERNEL__ -I./include -I$(KERNELDIR)/include -I$(KERNELDIR)/arch/ia64/include ia64/asm-offsets.c
diff --git a/kvm/kernel/ia64/external-module-compat.h b/kvm/kernel/ia64/external-module-compat.h
new file mode 100644
index 000000000..3fcade1a5
--- /dev/null
+++ b/kvm/kernel/ia64/external-module-compat.h
@@ -0,0 +1,39 @@
+/*
+ * Compatibility header for building as an external module.
+ */
+
+#ifndef __ASSEMBLY__
+#include <linux/version.h>
+
+#include "../external-module-compat-comm.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+#error "KVM/IA-64 Can't be compiled if kernel version < 2.6.26"
+#endif
+
+#ifndef CONFIG_PREEMPT_NOTIFIERS
+/*Now, Just print an error message if no preempt notifiers configured!!
+ TODO: Implement it later! */
+#error "KVM/IA-64 depends on preempt notifiers in kernel."
+#endif
+
+/* smp_call_function() lost an argument in 2.6.27. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+
+#define kvm_smp_call_function(func, info, wait) smp_call_function(func, info, 0, wait)
+
+#else
+
+#define kvm_smp_call_function(func, info, wait) smp_call_function(func, info, wait)
+
+#endif
+
+/*There is no struct fdesc definition <2.6.27*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+struct fdesc {
+ uint64_t ip;
+ uint64_t gp;
+};
+#endif
+
+#endif
diff --git a/kvm/kernel/ia64/hack-module.awk b/kvm/kernel/ia64/hack-module.awk
new file mode 100644
index 000000000..3dd2260c2
--- /dev/null
+++ b/kvm/kernel/ia64/hack-module.awk
@@ -0,0 +1,25 @@
+BEGIN { split("INIT_WORK on_each_cpu smp_call_function " \
+ "request_irq", compat_apis); }
+
+/MODULE_AUTHOR/ {
+ printf("MODULE_INFO(version, \"%s\");\n", version)
+}
+
+{ sub(/..\/..\/..\/lib\/vsprintf\.c/, "vsprintf.c") }
+{ sub(/..\/..\/..\/lib\/ctype\.c/, "ctype.c") }
+/#undef CONFIG_MODULES/ { $0 = "" }
+
+{
+ for (i in compat_apis) {
+ ident = compat_apis[i]
+ sub("\\<" ident "\\>", "kvm_" ident)
+ }
+}
+
+/#include <linux\/compiler.h>/ { $0 = "" }
+
+{ sub(/linux\/mm_types\.h/, "linux/mm.h") }
+
+{ sub(/\<__user\>/, " ") }
+
+{ print }
diff --git a/kvm/kernel/include-compat/asm-x86/asm.h b/kvm/kernel/include-compat/asm-x86/asm.h
new file mode 100644
index 000000000..3ad6aab9b
--- /dev/null
+++ b/kvm/kernel/include-compat/asm-x86/asm.h
@@ -0,0 +1,3 @@
+/*
+ * Empty file to satisfy #include <linux/asm.h> for older kernels.
+ */
diff --git a/kvm/kernel/include-compat/asm-x86/cmpxchg.h b/kvm/kernel/include-compat/asm-x86/cmpxchg.h
new file mode 100644
index 000000000..68daeebc6
--- /dev/null
+++ b/kvm/kernel/include-compat/asm-x86/cmpxchg.h
@@ -0,0 +1,3 @@
+/*
+ * Empty file to satisfy #include <linux/cmpxchg.h> for older kernels.
+ */
diff --git a/kvm/kernel/include-compat/asm-x86/msidef.h b/kvm/kernel/include-compat/asm-x86/msidef.h
new file mode 100644
index 000000000..6706b3006
--- /dev/null
+++ b/kvm/kernel/include-compat/asm-x86/msidef.h
@@ -0,0 +1,55 @@
+#ifndef _ASM_X86_MSIDEF_H
+#define _ASM_X86_MSIDEF_H
+
+/*
+ * Constants for Intel APIC based MSI messages.
+ */
+
+/*
+ * Shifts for MSI data
+ */
+
+#define MSI_DATA_VECTOR_SHIFT 0
+#define MSI_DATA_VECTOR_MASK 0x000000ff
+#define MSI_DATA_VECTOR(v) (((v) << MSI_DATA_VECTOR_SHIFT) & \
+ MSI_DATA_VECTOR_MASK)
+
+#define MSI_DATA_DELIVERY_MODE_SHIFT 8
+#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_MODE_SHIFT)
+#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_MODE_SHIFT)
+
+#define MSI_DATA_LEVEL_SHIFT 14
+#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT)
+#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT)
+
+#define MSI_DATA_TRIGGER_SHIFT 15
+#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT)
+#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT)
+
+/*
+ * Shift/mask fields for msi address
+ */
+
+#define MSI_ADDR_BASE_HI 0
+#define MSI_ADDR_BASE_LO 0xfee00000
+
+#define MSI_ADDR_DEST_MODE_SHIFT 2
+#define MSI_ADDR_DEST_MODE_PHYSICAL (0 << MSI_ADDR_DEST_MODE_SHIFT)
+#define MSI_ADDR_DEST_MODE_LOGICAL (1 << MSI_ADDR_DEST_MODE_SHIFT)
+
+#define MSI_ADDR_REDIRECTION_SHIFT 3
+#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT)
+ /* dedicated cpu */
+#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT)
+ /* lowest priority */
+
+#define MSI_ADDR_DEST_ID_SHIFT 12
+#define MSI_ADDR_DEST_ID_MASK 0x00ffff0
+#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & \
+ MSI_ADDR_DEST_ID_MASK)
+
+#define MSI_ADDR_IR_EXT_INT (1 << 4)
+#define MSI_ADDR_IR_SHV (1 << 3)
+#define MSI_ADDR_IR_INDEX1(index) ((index & 0x8000) >> 13)
+#define MSI_ADDR_IR_INDEX2(index) ((index & 0x7fff) << 5)
+#endif /* _ASM_X86_MSIDEF_H */
diff --git a/kvm/kernel/include-compat/asm-x86/msr-index.h b/kvm/kernel/include-compat/asm-x86/msr-index.h
new file mode 100644
index 000000000..e7625b1d9
--- /dev/null
+++ b/kvm/kernel/include-compat/asm-x86/msr-index.h
@@ -0,0 +1,339 @@
+#ifndef _ASM_X86_MSR_INDEX_H
+#define _ASM_X86_MSR_INDEX_H
+
+/* CPU model specific register (MSR) numbers */
+
+/* x86-64 specific MSRs */
+#define MSR_EFER 0xc0000080 /* extended feature register */
+#define MSR_STAR 0xc0000081 /* legacy mode SYSCALL target */
+#define MSR_LSTAR 0xc0000082 /* long mode SYSCALL target */
+#define MSR_CSTAR 0xc0000083 /* compat mode SYSCALL target */
+#define MSR_SYSCALL_MASK 0xc0000084 /* EFLAGS mask for syscall */
+#define MSR_FS_BASE 0xc0000100 /* 64bit FS base */
+#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */
+#define MSR_KERNEL_GS_BASE 0xc0000102 /* SwapGS GS shadow */
+
+/* EFER bits: */
+#define _EFER_SCE 0 /* SYSCALL/SYSRET */
+#define _EFER_LME 8 /* Long mode enable */
+#define _EFER_LMA 10 /* Long mode active (read-only) */
+#define _EFER_NX 11 /* No execute enable */
+#define _EFER_SVME 12 /* Enable virtualization */
+
+#define EFER_SCE (1<<_EFER_SCE)
+#define EFER_LME (1<<_EFER_LME)
+#define EFER_LMA (1<<_EFER_LMA)
+#define EFER_NX (1<<_EFER_NX)
+#define EFER_SVME (1<<_EFER_SVME)
+
+/* Intel MSRs. Some also available on other CPUs */
+#define MSR_IA32_PERFCTR0 0x000000c1
+#define MSR_IA32_PERFCTR1 0x000000c2
+#define MSR_FSB_FREQ 0x000000cd
+
+#define MSR_MTRRcap 0x000000fe
+#define MSR_IA32_BBL_CR_CTL 0x00000119
+
+#define MSR_IA32_SYSENTER_CS 0x00000174
+#define MSR_IA32_SYSENTER_ESP 0x00000175
+#define MSR_IA32_SYSENTER_EIP 0x00000176
+
+#define MSR_IA32_MCG_CAP 0x00000179
+#define MSR_IA32_MCG_STATUS 0x0000017a
+#define MSR_IA32_MCG_CTL 0x0000017b
+
+#define MSR_IA32_PEBS_ENABLE 0x000003f1
+#define MSR_IA32_DS_AREA 0x00000600
+#define MSR_IA32_PERF_CAPABILITIES 0x00000345
+
+#define MSR_MTRRfix64K_00000 0x00000250
+#define MSR_MTRRfix16K_80000 0x00000258
+#define MSR_MTRRfix16K_A0000 0x00000259
+#define MSR_MTRRfix4K_C0000 0x00000268
+#define MSR_MTRRfix4K_C8000 0x00000269
+#define MSR_MTRRfix4K_D0000 0x0000026a
+#define MSR_MTRRfix4K_D8000 0x0000026b
+#define MSR_MTRRfix4K_E0000 0x0000026c
+#define MSR_MTRRfix4K_E8000 0x0000026d
+#define MSR_MTRRfix4K_F0000 0x0000026e
+#define MSR_MTRRfix4K_F8000 0x0000026f
+#define MSR_MTRRdefType 0x000002ff
+
+#define MSR_IA32_CR_PAT 0x00000277
+
+#define MSR_IA32_DEBUGCTLMSR 0x000001d9
+#define MSR_IA32_LASTBRANCHFROMIP 0x000001db
+#define MSR_IA32_LASTBRANCHTOIP 0x000001dc
+#define MSR_IA32_LASTINTFROMIP 0x000001dd
+#define MSR_IA32_LASTINTTOIP 0x000001de
+
+/* DEBUGCTLMSR bits (others vary by model): */
+#define _DEBUGCTLMSR_LBR 0 /* last branch recording */
+#define _DEBUGCTLMSR_BTF 1 /* single-step on branches */
+
+#define DEBUGCTLMSR_LBR (1UL << _DEBUGCTLMSR_LBR)
+#define DEBUGCTLMSR_BTF (1UL << _DEBUGCTLMSR_BTF)
+
+#define MSR_IA32_MC0_CTL 0x00000400
+#define MSR_IA32_MC0_STATUS 0x00000401
+#define MSR_IA32_MC0_ADDR 0x00000402
+#define MSR_IA32_MC0_MISC 0x00000403
+
+#define MSR_P6_PERFCTR0 0x000000c1
+#define MSR_P6_PERFCTR1 0x000000c2
+#define MSR_P6_EVNTSEL0 0x00000186
+#define MSR_P6_EVNTSEL1 0x00000187
+
+/* AMD64 MSRs. Not complete. See the architecture manual for a more
+ complete list. */
+
+#define MSR_AMD64_NB_CFG 0xc001001f
+#define MSR_AMD64_IBSFETCHCTL 0xc0011030
+#define MSR_AMD64_IBSFETCHLINAD 0xc0011031
+#define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032
+#define MSR_AMD64_IBSOPCTL 0xc0011033
+#define MSR_AMD64_IBSOPRIP 0xc0011034
+#define MSR_AMD64_IBSOPDATA 0xc0011035
+#define MSR_AMD64_IBSOPDATA2 0xc0011036
+#define MSR_AMD64_IBSOPDATA3 0xc0011037
+#define MSR_AMD64_IBSDCLINAD 0xc0011038
+#define MSR_AMD64_IBSDCPHYSAD 0xc0011039
+#define MSR_AMD64_IBSCTL 0xc001103a
+
+/* Fam 10h MSRs */
+#define MSR_FAM10H_MMIO_CONF_BASE 0xc0010058
+#define FAM10H_MMIO_CONF_ENABLE (1<<0)
+#define FAM10H_MMIO_CONF_BUSRANGE_MASK 0xf
+#define FAM10H_MMIO_CONF_BUSRANGE_SHIFT 2
+#define FAM10H_MMIO_CONF_BASE_MASK 0xfffffff
+#define FAM10H_MMIO_CONF_BASE_SHIFT 20
+
+/* K8 MSRs */
+#define MSR_K8_TOP_MEM1 0xc001001a
+#define MSR_K8_TOP_MEM2 0xc001001d
+#define MSR_K8_SYSCFG 0xc0010010
+#define MSR_K8_HWCR 0xc0010015
+#define MSR_K8_INT_PENDING_MSG 0xc0010055
+/* C1E active bits in int pending message */
+#define K8_INTP_C1E_ACTIVE_MASK 0x18000000
+#define MSR_K8_TSEG_ADDR 0xc0010112
+#define K8_MTRRFIXRANGE_DRAM_ENABLE 0x00040000 /* MtrrFixDramEn bit */
+#define K8_MTRRFIXRANGE_DRAM_MODIFY 0x00080000 /* MtrrFixDramModEn bit */
+#define K8_MTRR_RDMEM_WRMEM_MASK 0x18181818 /* Mask: RdMem|WrMem */
+
+/* K7 MSRs */
+#define MSR_K7_EVNTSEL0 0xc0010000
+#define MSR_K7_PERFCTR0 0xc0010004
+#define MSR_K7_EVNTSEL1 0xc0010001
+#define MSR_K7_PERFCTR1 0xc0010005
+#define MSR_K7_EVNTSEL2 0xc0010002
+#define MSR_K7_PERFCTR2 0xc0010006
+#define MSR_K7_EVNTSEL3 0xc0010003
+#define MSR_K7_PERFCTR3 0xc0010007
+#define MSR_K7_CLK_CTL 0xc001001b
+#define MSR_K7_HWCR 0xc0010015
+#define MSR_K7_FID_VID_CTL 0xc0010041
+#define MSR_K7_FID_VID_STATUS 0xc0010042
+
+/* K6 MSRs */
+#define MSR_K6_EFER 0xc0000080
+#define MSR_K6_STAR 0xc0000081
+#define MSR_K6_WHCR 0xc0000082
+#define MSR_K6_UWCCR 0xc0000085
+#define MSR_K6_EPMR 0xc0000086
+#define MSR_K6_PSOR 0xc0000087
+#define MSR_K6_PFIR 0xc0000088
+
+/* Centaur-Hauls/IDT defined MSRs. */
+#define MSR_IDT_FCR1 0x00000107
+#define MSR_IDT_FCR2 0x00000108
+#define MSR_IDT_FCR3 0x00000109
+#define MSR_IDT_FCR4 0x0000010a
+
+#define MSR_IDT_MCR0 0x00000110
+#define MSR_IDT_MCR1 0x00000111
+#define MSR_IDT_MCR2 0x00000112
+#define MSR_IDT_MCR3 0x00000113
+#define MSR_IDT_MCR4 0x00000114
+#define MSR_IDT_MCR5 0x00000115
+#define MSR_IDT_MCR6 0x00000116
+#define MSR_IDT_MCR7 0x00000117
+#define MSR_IDT_MCR_CTRL 0x00000120
+
+/* VIA Cyrix defined MSRs*/
+#define MSR_VIA_FCR 0x00001107
+#define MSR_VIA_LONGHAUL 0x0000110a
+#define MSR_VIA_RNG 0x0000110b
+#define MSR_VIA_BCR2 0x00001147
+
+/* Transmeta defined MSRs */
+#define MSR_TMTA_LONGRUN_CTRL 0x80868010
+#define MSR_TMTA_LONGRUN_FLAGS 0x80868011
+#define MSR_TMTA_LRTI_READOUT 0x80868018
+#define MSR_TMTA_LRTI_VOLT_MHZ 0x8086801a
+
+/* Intel defined MSRs. */
+#define MSR_IA32_P5_MC_ADDR 0x00000000
+#define MSR_IA32_P5_MC_TYPE 0x00000001
+#define MSR_IA32_TSC 0x00000010
+#define MSR_IA32_PLATFORM_ID 0x00000017
+#define MSR_IA32_EBL_CR_POWERON 0x0000002a
+#define MSR_IA32_FEATURE_CONTROL 0x0000003a
+
+#define FEATURE_CONTROL_LOCKED (1<<0)
+#define FEATURE_CONTROL_VMXON_ENABLED (1<<2)
+
+#define MSR_IA32_APICBASE 0x0000001b
+#define MSR_IA32_APICBASE_BSP (1<<8)
+#define MSR_IA32_APICBASE_ENABLE (1<<11)
+#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
+
+#define MSR_IA32_UCODE_WRITE 0x00000079
+#define MSR_IA32_UCODE_REV 0x0000008b
+
+#define MSR_IA32_PERF_STATUS 0x00000198
+#define MSR_IA32_PERF_CTL 0x00000199
+
+#define MSR_IA32_MPERF 0x000000e7
+#define MSR_IA32_APERF 0x000000e8
+
+#define MSR_IA32_THERM_CONTROL 0x0000019a
+#define MSR_IA32_THERM_INTERRUPT 0x0000019b
+#define MSR_IA32_THERM_STATUS 0x0000019c
+#define MSR_IA32_MISC_ENABLE 0x000001a0
+
+/* Intel Model 6 */
+#define MSR_P6_EVNTSEL0 0x00000186
+#define MSR_P6_EVNTSEL1 0x00000187
+
+/* P4/Xeon+ specific */
+#define MSR_IA32_MCG_EAX 0x00000180
+#define MSR_IA32_MCG_EBX 0x00000181
+#define MSR_IA32_MCG_ECX 0x00000182
+#define MSR_IA32_MCG_EDX 0x00000183
+#define MSR_IA32_MCG_ESI 0x00000184
+#define MSR_IA32_MCG_EDI 0x00000185
+#define MSR_IA32_MCG_EBP 0x00000186
+#define MSR_IA32_MCG_ESP 0x00000187
+#define MSR_IA32_MCG_EFLAGS 0x00000188
+#define MSR_IA32_MCG_EIP 0x00000189
+#define MSR_IA32_MCG_RESERVED 0x0000018a
+
+/* Pentium IV performance counter MSRs */
+#define MSR_P4_BPU_PERFCTR0 0x00000300
+#define MSR_P4_BPU_PERFCTR1 0x00000301
+#define MSR_P4_BPU_PERFCTR2 0x00000302
+#define MSR_P4_BPU_PERFCTR3 0x00000303
+#define MSR_P4_MS_PERFCTR0 0x00000304
+#define MSR_P4_MS_PERFCTR1 0x00000305
+#define MSR_P4_MS_PERFCTR2 0x00000306
+#define MSR_P4_MS_PERFCTR3 0x00000307
+#define MSR_P4_FLAME_PERFCTR0 0x00000308
+#define MSR_P4_FLAME_PERFCTR1 0x00000309
+#define MSR_P4_FLAME_PERFCTR2 0x0000030a
+#define MSR_P4_FLAME_PERFCTR3 0x0000030b
+#define MSR_P4_IQ_PERFCTR0 0x0000030c
+#define MSR_P4_IQ_PERFCTR1 0x0000030d
+#define MSR_P4_IQ_PERFCTR2 0x0000030e
+#define MSR_P4_IQ_PERFCTR3 0x0000030f
+#define MSR_P4_IQ_PERFCTR4 0x00000310
+#define MSR_P4_IQ_PERFCTR5 0x00000311
+#define MSR_P4_BPU_CCCR0 0x00000360
+#define MSR_P4_BPU_CCCR1 0x00000361
+#define MSR_P4_BPU_CCCR2 0x00000362
+#define MSR_P4_BPU_CCCR3 0x00000363
+#define MSR_P4_MS_CCCR0 0x00000364
+#define MSR_P4_MS_CCCR1 0x00000365
+#define MSR_P4_MS_CCCR2 0x00000366
+#define MSR_P4_MS_CCCR3 0x00000367
+#define MSR_P4_FLAME_CCCR0 0x00000368
+#define MSR_P4_FLAME_CCCR1 0x00000369
+#define MSR_P4_FLAME_CCCR2 0x0000036a
+#define MSR_P4_FLAME_CCCR3 0x0000036b
+#define MSR_P4_IQ_CCCR0 0x0000036c
+#define MSR_P4_IQ_CCCR1 0x0000036d
+#define MSR_P4_IQ_CCCR2 0x0000036e
+#define MSR_P4_IQ_CCCR3 0x0000036f
+#define MSR_P4_IQ_CCCR4 0x00000370
+#define MSR_P4_IQ_CCCR5 0x00000371
+#define MSR_P4_ALF_ESCR0 0x000003ca
+#define MSR_P4_ALF_ESCR1 0x000003cb
+#define MSR_P4_BPU_ESCR0 0x000003b2
+#define MSR_P4_BPU_ESCR1 0x000003b3
+#define MSR_P4_BSU_ESCR0 0x000003a0
+#define MSR_P4_BSU_ESCR1 0x000003a1
+#define MSR_P4_CRU_ESCR0 0x000003b8
+#define MSR_P4_CRU_ESCR1 0x000003b9
+#define MSR_P4_CRU_ESCR2 0x000003cc
+#define MSR_P4_CRU_ESCR3 0x000003cd
+#define MSR_P4_CRU_ESCR4 0x000003e0
+#define MSR_P4_CRU_ESCR5 0x000003e1
+#define MSR_P4_DAC_ESCR0 0x000003a8
+#define MSR_P4_DAC_ESCR1 0x000003a9
+#define MSR_P4_FIRM_ESCR0 0x000003a4
+#define MSR_P4_FIRM_ESCR1 0x000003a5
+#define MSR_P4_FLAME_ESCR0 0x000003a6
+#define MSR_P4_FLAME_ESCR1 0x000003a7
+#define MSR_P4_FSB_ESCR0 0x000003a2
+#define MSR_P4_FSB_ESCR1 0x000003a3
+#define MSR_P4_IQ_ESCR0 0x000003ba
+#define MSR_P4_IQ_ESCR1 0x000003bb
+#define MSR_P4_IS_ESCR0 0x000003b4
+#define MSR_P4_IS_ESCR1 0x000003b5
+#define MSR_P4_ITLB_ESCR0 0x000003b6
+#define MSR_P4_ITLB_ESCR1 0x000003b7
+#define MSR_P4_IX_ESCR0 0x000003c8
+#define MSR_P4_IX_ESCR1 0x000003c9
+#define MSR_P4_MOB_ESCR0 0x000003aa
+#define MSR_P4_MOB_ESCR1 0x000003ab
+#define MSR_P4_MS_ESCR0 0x000003c0
+#define MSR_P4_MS_ESCR1 0x000003c1
+#define MSR_P4_PMH_ESCR0 0x000003ac
+#define MSR_P4_PMH_ESCR1 0x000003ad
+#define MSR_P4_RAT_ESCR0 0x000003bc
+#define MSR_P4_RAT_ESCR1 0x000003bd
+#define MSR_P4_SAAT_ESCR0 0x000003ae
+#define MSR_P4_SAAT_ESCR1 0x000003af
+#define MSR_P4_SSU_ESCR0 0x000003be
+#define MSR_P4_SSU_ESCR1 0x000003bf /* guess: not in manual */
+
+#define MSR_P4_TBPU_ESCR0 0x000003c2
+#define MSR_P4_TBPU_ESCR1 0x000003c3
+#define MSR_P4_TC_ESCR0 0x000003c4
+#define MSR_P4_TC_ESCR1 0x000003c5
+#define MSR_P4_U2L_ESCR0 0x000003b0
+#define MSR_P4_U2L_ESCR1 0x000003b1
+
+/* Intel Core-based CPU performance counters */
+#define MSR_CORE_PERF_FIXED_CTR0 0x00000309
+#define MSR_CORE_PERF_FIXED_CTR1 0x0000030a
+#define MSR_CORE_PERF_FIXED_CTR2 0x0000030b
+#define MSR_CORE_PERF_FIXED_CTR_CTRL 0x0000038d
+#define MSR_CORE_PERF_GLOBAL_STATUS 0x0000038e
+#define MSR_CORE_PERF_GLOBAL_CTRL 0x0000038f
+#define MSR_CORE_PERF_GLOBAL_OVF_CTRL 0x00000390
+
+/* Geode defined MSRs */
+#define MSR_GEODE_BUSCONT_CONF0 0x00001900
+
+/* Intel VT MSRs */
+#define MSR_IA32_VMX_BASIC 0x00000480
+#define MSR_IA32_VMX_PINBASED_CTLS 0x00000481
+#define MSR_IA32_VMX_PROCBASED_CTLS 0x00000482
+#define MSR_IA32_VMX_EXIT_CTLS 0x00000483
+#define MSR_IA32_VMX_ENTRY_CTLS 0x00000484
+#define MSR_IA32_VMX_MISC 0x00000485
+#define MSR_IA32_VMX_CR0_FIXED0 0x00000486
+#define MSR_IA32_VMX_CR0_FIXED1 0x00000487
+#define MSR_IA32_VMX_CR4_FIXED0 0x00000488
+#define MSR_IA32_VMX_CR4_FIXED1 0x00000489
+#define MSR_IA32_VMX_VMCS_ENUM 0x0000048a
+#define MSR_IA32_VMX_PROCBASED_CTLS2 0x0000048b
+#define MSR_IA32_VMX_EPT_VPID_CAP 0x0000048c
+
+/* AMD-V MSRs */
+
+#define MSR_VM_CR 0xc0010114
+#define MSR_VM_HSAVE_PA 0xc0010117
+
+#endif /* _ASM_X86_MSR_INDEX_H */
diff --git a/kvm/kernel/include-compat/asm-x86/pvclock-abi.h b/kvm/kernel/include-compat/asm-x86/pvclock-abi.h
new file mode 100644
index 000000000..6857f840b
--- /dev/null
+++ b/kvm/kernel/include-compat/asm-x86/pvclock-abi.h
@@ -0,0 +1,42 @@
+#ifndef _ASM_X86_PVCLOCK_ABI_H_
+#define _ASM_X86_PVCLOCK_ABI_H_
+#ifndef __ASSEMBLY__
+
+/*
+ * These structs MUST NOT be changed.
+ * They are the ABI between hypervisor and guest OS.
+ * Both Xen and KVM are using this.
+ *
+ * pvclock_vcpu_time_info holds the system time and the tsc timestamp
+ * of the last update. So the guest can use the tsc delta to get a
+ * more precise system time. There is one per virtual cpu.
+ *
+ * pvclock_wall_clock references the point in time when the system
+ * time was zero (usually boot time), thus the guest calculates the
+ * current wall clock by adding the system time.
+ *
+ * Protocol for the "version" fields is: hypervisor raises it (making
+ * it uneven) before it starts updating the fields and raises it again
+ * (making it even) when it is done. Thus the guest can make sure the
+ * time values it got are consistent by checking the version before
+ * and after reading them.
+ */
+
+struct pvclock_vcpu_time_info {
+ u32 version;
+ u32 pad0;
+ u64 tsc_timestamp;
+ u64 system_time;
+ u32 tsc_to_system_mul;
+ s8 tsc_shift;
+ u8 pad[3];
+} __attribute__((__packed__)); /* 32 bytes */
+
+struct pvclock_wall_clock {
+ u32 version;
+ u32 sec;
+ u32 nsec;
+} __attribute__((__packed__));
+
+#endif /* __ASSEMBLY__ */
+#endif /* _ASM_X86_PVCLOCK_ABI_H_ */
diff --git a/kvm/kernel/include-compat/linux/anon_inodes.h b/kvm/kernel/include-compat/linux/anon_inodes.h
new file mode 100644
index 000000000..7b6862f26
--- /dev/null
+++ b/kvm/kernel/include-compat/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
+
+struct file_operations;
+
+int anon_inode_getfd(const char *name, const struct file_operations *fops,
+ void *priv);
+
+#endif /* _LINUX_ANON_INODES_H */
diff --git a/kvm/kernel/include-compat/linux/intel-iommu.h b/kvm/kernel/include-compat/linux/intel-iommu.h
new file mode 100644
index 000000000..1490fc075
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/intel-iommu.h
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Author: Ashok Raj <ashok.raj@intel.com>
+ * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ */
+
+#ifndef _INTEL_IOMMU_H_
+#define _INTEL_IOMMU_H_
+
+#include <linux/types.h>
+#include <linux/msi.h>
+#include <linux/sysdev.h>
+#include "iova.h"
+#include <linux/io.h>
+
+/*
+ * We need a fixed PAGE_SIZE of 4K irrespective of
+ * arch PAGE_SIZE for IOMMU page tables.
+ */
+#define PAGE_SHIFT_4K (12)
+#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K)
+#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K)
+#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K)
+
+#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K)
+#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK)
+#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK)
+
+/*
+ * Intel IOMMU register specification per version 1.0 public spec.
+ */
+
+#define DMAR_VER_REG 0x0 /* Arch version supported by this IOMMU */
+#define DMAR_CAP_REG 0x8 /* Hardware supported capabilities */
+#define DMAR_ECAP_REG 0x10 /* Extended capabilities supported */
+#define DMAR_GCMD_REG 0x18 /* Global command register */
+#define DMAR_GSTS_REG 0x1c /* Global status register */
+#define DMAR_RTADDR_REG 0x20 /* Root entry table */
+#define DMAR_CCMD_REG 0x28 /* Context command reg */
+#define DMAR_FSTS_REG 0x34 /* Fault Status register */
+#define DMAR_FECTL_REG 0x38 /* Fault control register */
+#define DMAR_FEDATA_REG 0x3c /* Fault event interrupt data register */
+#define DMAR_FEADDR_REG 0x40 /* Fault event interrupt addr register */
+#define DMAR_FEUADDR_REG 0x44 /* Upper address register */
+#define DMAR_AFLOG_REG 0x58 /* Advanced Fault control */
+#define DMAR_PMEN_REG 0x64 /* Enable Protected Memory Region */
+#define DMAR_PLMBASE_REG 0x68 /* PMRR Low addr */
+#define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */
+#define DMAR_PHMBASE_REG 0x70 /* pmrr high base addr */
+#define DMAR_PHMLIMIT_REG 0x78 /* pmrr high limit */
+
+#define OFFSET_STRIDE (9)
+/*
+#define dmar_readl(dmar, reg) readl(dmar + reg)
+#define dmar_readq(dmar, reg) ({ \
+ u32 lo, hi; \
+ lo = readl(dmar + reg); \
+ hi = readl(dmar + reg + 4); \
+ (((u64) hi) << 32) + lo; })
+*/
+static inline u64 dmar_readq(void __iomem *addr)
+{
+ u32 lo, hi;
+ lo = readl(addr);
+ hi = readl(addr + 4);
+ return (((u64) hi) << 32) + lo;
+}
+
+static inline void dmar_writeq(void __iomem *addr, u64 val)
+{
+ writel((u32)val, addr);
+ writel((u32)(val >> 32), addr + 4);
+}
+
+#define DMAR_VER_MAJOR(v) (((v) & 0xf0) >> 4)
+#define DMAR_VER_MINOR(v) ((v) & 0x0f)
+
+/*
+ * Decoding Capability Register
+ */
+#define cap_read_drain(c) (((c) >> 55) & 1)
+#define cap_write_drain(c) (((c) >> 54) & 1)
+#define cap_max_amask_val(c) (((c) >> 48) & 0x3f)
+#define cap_num_fault_regs(c) ((((c) >> 40) & 0xff) + 1)
+#define cap_pgsel_inv(c) (((c) >> 39) & 1)
+
+#define cap_super_page_val(c) (((c) >> 34) & 0xf)
+#define cap_super_offset(c) (((find_first_bit(&cap_super_page_val(c), 4)) \
+ * OFFSET_STRIDE) + 21)
+
+#define cap_fault_reg_offset(c) ((((c) >> 24) & 0x3ff) * 16)
+#define cap_max_fault_reg_offset(c) \
+ (cap_fault_reg_offset(c) + cap_num_fault_regs(c) * 16)
+
+#define cap_zlr(c) (((c) >> 22) & 1)
+#define cap_isoch(c) (((c) >> 23) & 1)
+#define cap_mgaw(c) ((((c) >> 16) & 0x3f) + 1)
+#define cap_sagaw(c) (((c) >> 8) & 0x1f)
+#define cap_caching_mode(c) (((c) >> 7) & 1)
+#define cap_phmr(c) (((c) >> 6) & 1)
+#define cap_plmr(c) (((c) >> 5) & 1)
+#define cap_rwbf(c) (((c) >> 4) & 1)
+#define cap_afl(c) (((c) >> 3) & 1)
+#define cap_ndoms(c) (((unsigned long)1) << (4 + 2 * ((c) & 0x7)))
+/*
+ * Extended Capability Register
+ */
+
+#define ecap_niotlb_iunits(e) ((((e) >> 24) & 0xff) + 1)
+#define ecap_iotlb_offset(e) ((((e) >> 8) & 0x3ff) * 16)
+#define ecap_max_iotlb_offset(e) \
+ (ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16)
+#define ecap_coherent(e) ((e) & 0x1)
+
+
+/* IOTLB_REG */
+#define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60)
+#define DMA_TLB_DSI_FLUSH (((u64)2) << 60)
+#define DMA_TLB_PSI_FLUSH (((u64)3) << 60)
+#define DMA_TLB_IIRG(type) ((type >> 60) & 7)
+#define DMA_TLB_IAIG(val) (((val) >> 57) & 7)
+#define DMA_TLB_READ_DRAIN (((u64)1) << 49)
+#define DMA_TLB_WRITE_DRAIN (((u64)1) << 48)
+#define DMA_TLB_DID(id) (((u64)((id) & 0xffff)) << 32)
+#define DMA_TLB_IVT (((u64)1) << 63)
+#define DMA_TLB_IH_NONLEAF (((u64)1) << 6)
+#define DMA_TLB_MAX_SIZE (0x3f)
+
+/* PMEN_REG */
+#define DMA_PMEN_EPM (((u32)1)<<31)
+#define DMA_PMEN_PRS (((u32)1)<<0)
+
+/* GCMD_REG */
+#define DMA_GCMD_TE (((u32)1) << 31)
+#define DMA_GCMD_SRTP (((u32)1) << 30)
+#define DMA_GCMD_SFL (((u32)1) << 29)
+#define DMA_GCMD_EAFL (((u32)1) << 28)
+#define DMA_GCMD_WBF (((u32)1) << 27)
+
+/* GSTS_REG */
+#define DMA_GSTS_TES (((u32)1) << 31)
+#define DMA_GSTS_RTPS (((u32)1) << 30)
+#define DMA_GSTS_FLS (((u32)1) << 29)
+#define DMA_GSTS_AFLS (((u32)1) << 28)
+#define DMA_GSTS_WBFS (((u32)1) << 27)
+
+/* CCMD_REG */
+#define DMA_CCMD_ICC (((u64)1) << 63)
+#define DMA_CCMD_GLOBAL_INVL (((u64)1) << 61)
+#define DMA_CCMD_DOMAIN_INVL (((u64)2) << 61)
+#define DMA_CCMD_DEVICE_INVL (((u64)3) << 61)
+#define DMA_CCMD_FM(m) (((u64)((m) & 0x3)) << 32)
+#define DMA_CCMD_MASK_NOBIT 0
+#define DMA_CCMD_MASK_1BIT 1
+#define DMA_CCMD_MASK_2BIT 2
+#define DMA_CCMD_MASK_3BIT 3
+#define DMA_CCMD_SID(s) (((u64)((s) & 0xffff)) << 16)
+#define DMA_CCMD_DID(d) ((u64)((d) & 0xffff))
+
+/* FECTL_REG */
+#define DMA_FECTL_IM (((u32)1) << 31)
+
+/* FSTS_REG */
+#define DMA_FSTS_PPF ((u32)2)
+#define DMA_FSTS_PFO ((u32)1)
+#define dma_fsts_fault_record_index(s) (((s) >> 8) & 0xff)
+
+/* FRCD_REG, 32 bits access */
+#define DMA_FRCD_F (((u32)1) << 31)
+#define dma_frcd_type(d) ((d >> 30) & 1)
+#define dma_frcd_fault_reason(c) (c & 0xff)
+#define dma_frcd_source_id(c) (c & 0xffff)
+#define dma_frcd_page_addr(d) (d & (((u64)-1) << 12)) /* low 64 bit */
+
+/*
+ * 0: Present
+ * 1-11: Reserved
+ * 12-63: Context Ptr (12 - (haw-1))
+ * 64-127: Reserved
+ */
+struct root_entry {
+ u64 val;
+ u64 rsvd1;
+};
+#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry))
+static inline bool root_present(struct root_entry *root)
+{
+ return (root->val & 1);
+}
+static inline void set_root_present(struct root_entry *root)
+{
+ root->val |= 1;
+}
+static inline void set_root_value(struct root_entry *root, unsigned long value)
+{
+ root->val |= value & PAGE_MASK_4K;
+}
+
+struct context_entry;
+static inline struct context_entry *
+get_context_addr_from_root(struct root_entry *root)
+{
+ return (struct context_entry *)
+ (root_present(root)?phys_to_virt(
+ root->val & PAGE_MASK_4K):
+ NULL);
+}
+
+/*
+ * low 64 bits:
+ * 0: present
+ * 1: fault processing disable
+ * 2-3: translation type
+ * 12-63: address space root
+ * high 64 bits:
+ * 0-2: address width
+ * 3-6: aval
+ * 8-23: domain id
+ */
+struct context_entry {
+ u64 lo;
+ u64 hi;
+};
+#define context_present(c) ((c).lo & 1)
+#define context_fault_disable(c) (((c).lo >> 1) & 1)
+#define context_translation_type(c) (((c).lo >> 2) & 3)
+#define context_address_root(c) ((c).lo & PAGE_MASK_4K)
+#define context_address_width(c) ((c).hi & 7)
+#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1))
+
+#define context_set_present(c) do {(c).lo |= 1;} while (0)
+#define context_set_fault_enable(c) \
+ do {(c).lo &= (((u64)-1) << 2) | 1;} while (0)
+#define context_set_translation_type(c, val) \
+ do { \
+ (c).lo &= (((u64)-1) << 4) | 3; \
+ (c).lo |= ((val) & 3) << 2; \
+ } while (0)
+#define CONTEXT_TT_MULTI_LEVEL 0
+#define context_set_address_root(c, val) \
+ do {(c).lo |= (val) & PAGE_MASK_4K;} while (0)
+#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0)
+#define context_set_domain_id(c, val) \
+ do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0)
+#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0)
+
+/*
+ * 0: readable
+ * 1: writable
+ * 2-6: reserved
+ * 7: super page
+ * 8-11: available
+ * 12-63: Host physcial address
+ */
+struct dma_pte {
+ u64 val;
+};
+#define dma_clear_pte(p) do {(p).val = 0;} while (0)
+
+#define DMA_PTE_READ (1)
+#define DMA_PTE_WRITE (2)
+
+#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0)
+#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0)
+#define dma_set_pte_prot(p, prot) \
+ do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
+#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
+#define dma_set_pte_addr(p, addr) do {\
+ (p).val |= ((addr) & PAGE_MASK_4K); } while (0)
+#define dma_pte_present(p) (((p).val & 3) != 0)
+
+struct intel_iommu;
+
+struct dmar_domain {
+ int id; /* domain id */
+ struct intel_iommu *iommu; /* back pointer to owning iommu */
+
+ struct list_head devices; /* all devices' list */
+ struct iova_domain iovad; /* iova's that belong to this domain */
+
+ struct dma_pte *pgd; /* virtual address */
+ spinlock_t mapping_lock; /* page table lock */
+ int gaw; /* max guest address width */
+
+ /* adjusted guest address width, 0 is level 2 30-bit */
+ int agaw;
+
+#define DOMAIN_FLAG_MULTIPLE_DEVICES 1
+ int flags;
+};
+
+/* PCI domain-device relationship */
+struct device_domain_info {
+ struct list_head link; /* link to domain siblings */
+ struct list_head global; /* link to global list */
+ u8 bus; /* PCI bus numer */
+ u8 devfn; /* PCI devfn number */
+ struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
+ struct dmar_domain *domain; /* pointer to domain */
+};
+
+extern int init_dmars(void);
+
+struct intel_iommu {
+ void __iomem *reg; /* Pointer to hardware regs, virtual addr */
+ u64 cap;
+ u64 ecap;
+ unsigned long *domain_ids; /* bitmap of domains */
+ struct dmar_domain **domains; /* ptr to domains */
+ int seg;
+ u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */
+ spinlock_t lock; /* protect context, domain ids */
+ spinlock_t register_lock; /* protect register handling */
+ struct root_entry *root_entry; /* virtual address */
+
+ unsigned int irq;
+ unsigned char name[7]; /* Device Name */
+ struct msi_msg saved_msg;
+ struct sys_device sysdev;
+};
+
+#ifndef CONFIG_DMAR_GFX_WA
+static inline void iommu_prepare_gfx_mapping(void)
+{
+ return;
+}
+#endif /* !CONFIG_DMAR_GFX_WA */
+
+void intel_iommu_domain_exit(struct dmar_domain *domain);
+struct dmar_domain *intel_iommu_domain_alloc(struct pci_dev *pdev);
+int intel_iommu_context_mapping(struct dmar_domain *domain,
+ struct pci_dev *pdev);
+int intel_iommu_page_mapping(struct dmar_domain *domain, dma_addr_t iova,
+ u64 hpa, size_t size, int prot);
+void intel_iommu_detach_dev(struct dmar_domain *domain, u8 bus, u8 devfn);
+struct dmar_domain *intel_iommu_find_domain(struct pci_dev *pdev);
+int intel_iommu_found(void);
+u64 intel_iommu_iova_to_pfn(struct dmar_domain *domain, u64 iova);
+
+#endif
diff --git a/kvm/kernel/include-compat/linux/iova.h b/kvm/kernel/include-compat/linux/iova.h
new file mode 100644
index 000000000..228f6c94b
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/iova.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006, Intel Corporation.
+ *
+ * This file is released under the GPLv2.
+ *
+ * Copyright (C) 2006-2008 Intel Corporation
+ * Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ *
+ */
+
+#ifndef _IOVA_H_
+#define _IOVA_H_
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/rbtree.h>
+#include <linux/dma-mapping.h>
+
+/* IO virtual address start page frame number */
+#define IOVA_START_PFN (1)
+
+/* iova structure */
+struct iova {
+ struct rb_node node;
+ unsigned long pfn_hi; /* IOMMU dish out addr hi */
+ unsigned long pfn_lo; /* IOMMU dish out addr lo */
+};
+
+/* holds all the iova translations for a domain */
+struct iova_domain {
+ spinlock_t iova_alloc_lock;/* Lock to protect iova allocation */
+ spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */
+ struct rb_root rbroot; /* iova domain rbtree root */
+ struct rb_node *cached32_node; /* Save last alloced node */
+ unsigned long dma_32bit_pfn;
+};
+
+struct iova *alloc_iova_mem(void);
+void free_iova_mem(struct iova *iova);
+void free_iova(struct iova_domain *iovad, unsigned long pfn);
+void __free_iova(struct iova_domain *iovad, struct iova *iova);
+struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
+ unsigned long limit_pfn,
+ bool size_aligned);
+struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
+ unsigned long pfn_hi);
+void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to);
+void init_iova_domain(struct iova_domain *iovad, unsigned long pfn_32bit);
+struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
+void put_iova_domain(struct iova_domain *iovad);
+
+#endif
diff --git a/kvm/kernel/include-compat/linux/magic.h b/kvm/kernel/include-compat/linux/magic.h
new file mode 100644
index 000000000..a9c6567fe
--- /dev/null
+++ b/kvm/kernel/include-compat/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-compat/linux/marker.h b/kvm/kernel/include-compat/linux/marker.h
new file mode 100644
index 000000000..ceef04f2c
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/marker.h
@@ -0,0 +1,119 @@
+/*
+ * Alternative file to satisfy #include <linux/marker.h> for older kernels.
+ */
+#ifndef _LINUX_MARKER_H
+#define _LINUX_MARKER_H
+
+/*
+ * Code markup for dynamic and static tracing.
+ *
+ * See Documentation/marker.txt.
+ *
+ * (C) Copyright 2006 Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ * This file is released under the GPLv2.
+ * See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+
+struct module;
+struct marker;
+
+/**
+ * marker_probe_func - Type of a marker probe function
+ * @probe_private: probe private data
+ * @call_private: call site private data
+ * @fmt: format string
+ * @args: variable argument list pointer. Use a pointer to overcome C's
+ * inability to pass this around as a pointer in a portable manner in
+ * the callee otherwise.
+ *
+ * Type of marker probe functions. They receive the mdata and need to parse the
+ * format string to recover the variable argument list.
+ */
+typedef void marker_probe_func(void *probe_private, void *call_private,
+ const char *fmt, va_list *args);
+
+struct marker_probe_closure {
+ marker_probe_func *func; /* Callback */
+ void *probe_private; /* Private probe data */
+};
+
+struct marker {
+ const char *name; /* Marker name */
+ const char *format; /* Marker format string, describing the
+ * variable argument list.
+ */
+ char state; /* Marker state. */
+ char ptype; /* probe type : 0 : single, 1 : multi */
+ void (*call)(const struct marker *mdata, /* Probe wrapper */
+ void *call_private, const char *fmt, ...);
+ struct marker_probe_closure single;
+ struct marker_probe_closure *multi;
+} __attribute__((aligned(8)));
+
+#define __trace_mark(name, call_private, format, args...) \
+ __mark_check_format(format, ## args)
+static inline void marker_update_probe_range(struct marker *begin,
+ struct marker *end)
+{ }
+
+/**
+ * trace_mark - Marker
+ * @name: marker name, not quoted.
+ * @format: format string
+ * @args...: variable argument list
+ *
+ * Places a marker.
+ */
+#define trace_mark(name, format, args...) \
+ __trace_mark(name, NULL, format, ## args)
+
+/**
+ * MARK_NOARGS - Format string for a marker with no argument.
+ */
+#define MARK_NOARGS " "
+
+/* To be used for string format validity checking with gcc */
+static inline void __attribute__((format(printf,1,2)))
+___mark_check_format(const char *fmt, ...)
+{
+}
+
+#define __mark_check_format(format, args...) \
+ do { \
+ if (0) \
+ ___mark_check_format(format, ## args); \
+ } while (0)
+
+extern marker_probe_func __mark_empty_function;
+
+extern void marker_probe_cb(const struct marker *mdata,
+ void *call_private, const char *fmt, ...);
+extern void marker_probe_cb_noarg(const struct marker *mdata,
+ void *call_private, const char *fmt, ...);
+
+/*
+ * Connect a probe to a marker.
+ * private data pointer must be a valid allocated memory address, or NULL.
+ */
+extern int marker_probe_register(const char *name, const char *format,
+ marker_probe_func *probe, void *probe_private);
+
+/*
+ * Returns the private data given to marker_probe_register.
+ */
+extern int marker_probe_unregister(const char *name,
+ marker_probe_func *probe, void *probe_private);
+/*
+ * Unregister a marker by providing the registered private data.
+ */
+extern int marker_probe_unregister_private_data(marker_probe_func *probe,
+ void *probe_private);
+
+extern void *marker_get_private_data(const char *name, marker_probe_func *probe,
+ int num);
+
+#endif
+
diff --git a/kvm/kernel/include-compat/linux/math64.h b/kvm/kernel/include-compat/linux/math64.h
new file mode 100644
index 000000000..dc7c5812e
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/math64.h
@@ -0,0 +1,3 @@
+/*
+ * Empty file to satisfy #include <linux/math64.h> for older kernels.
+ */
diff --git a/kvm/kernel/include-compat/linux/mmu_notifier.h b/kvm/kernel/include-compat/linux/mmu_notifier.h
new file mode 100644
index 000000000..a6db4babe
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/mmu_notifier.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_MMU_NOTIFIER_H
+#define _LINUX_MMU_NOTIFIER_H
+
+struct mmu_notifier {};
+
+#endif
diff --git a/kvm/kernel/include-compat/linux/msi.h b/kvm/kernel/include-compat/linux/msi.h
new file mode 100644
index 000000000..8f2939227
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/msi.h
@@ -0,0 +1,50 @@
+#ifndef LINUX_MSI_H
+#define LINUX_MSI_H
+
+#include <linux/list.h>
+
+struct msi_msg {
+ u32 address_lo; /* low 32 bits of msi message address */
+ u32 address_hi; /* high 32 bits of msi message address */
+ u32 data; /* 16 bits of msi message data */
+};
+
+/* Helper functions */
+extern void mask_msi_irq(unsigned int irq);
+extern void unmask_msi_irq(unsigned int irq);
+extern void read_msi_msg(unsigned int irq, struct msi_msg *msg);
+extern void write_msi_msg(unsigned int irq, struct msi_msg *msg);
+
+struct msi_desc {
+ struct {
+ __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */
+ __u8 maskbit : 1; /* mask-pending bit supported ? */
+ __u8 masked : 1;
+ __u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */
+ __u8 pos; /* Location of the msi capability */
+ __u32 maskbits_mask; /* mask bits mask */
+ __u16 entry_nr; /* specific enabled entry */
+ unsigned default_irq; /* default pre-assigned irq */
+ }msi_attrib;
+
+ unsigned int irq;
+ struct list_head list;
+
+ void __iomem *mask_base;
+ struct pci_dev *dev;
+
+ /* Last set MSI message */
+ struct msi_msg msg;
+};
+
+/*
+ * The arch hook for setup up msi irqs
+ */
+int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc);
+void arch_teardown_msi_irq(unsigned int irq);
+extern int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
+extern void arch_teardown_msi_irqs(struct pci_dev *dev);
+extern int arch_msi_check_device(struct pci_dev* dev, int nvec, int type);
+
+
+#endif /* LINUX_MSI_H */
diff --git a/kvm/kernel/include-compat/linux/mutex.h b/kvm/kernel/include-compat/linux/mutex.h
new file mode 100644
index 000000000..449905c0d
--- /dev/null
+++ b/kvm/kernel/include-compat/linux/mutex.h
@@ -0,0 +1,3 @@
+/*
+ * 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..89b3d882c
--- /dev/null
+++ b/kvm/kernel/kvm-kmod.spec
@@ -0,0 +1,52 @@
+%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 ia64
+
+%description
+This kernel module provides support for virtual machines using hardware support
+(Intel VT-x&VT-i 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}-*.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
+%ifarch i386 x86_64
+%{moddir}/%{kmod_name}-amd.ko
+%endif
+%{moddir}/%{kmod_name}-intel.ko
+
+
+%changelog
diff --git a/kvm/kernel/powerpc/Makefile.pre b/kvm/kernel/powerpc/Makefile.pre
new file mode 100644
index 000000000..e38baf134
--- /dev/null
+++ b/kvm/kernel/powerpc/Makefile.pre
@@ -0,0 +1 @@
+prerequisite:
diff --git a/kvm/kernel/powerpc/hack-module.awk b/kvm/kernel/powerpc/hack-module.awk
new file mode 100644
index 000000000..570b034db
--- /dev/null
+++ b/kvm/kernel/powerpc/hack-module.awk
@@ -0,0 +1,5 @@
+/MODULE_AUTHOR/ {
+ printf("MODULE_INFO(version, \"%s\");\n", version)
+}
+
+{ print }
diff --git a/kvm/kernel/unifdef.h b/kvm/kernel/unifdef.h
new file mode 100644
index 000000000..6fc7be082
--- /dev/null
+++ b/kvm/kernel/unifdef.h
@@ -0,0 +1,40 @@
+#ifndef KVM_UNIFDEF_H
+#define KVM_UNIFDEF_H
+
+#ifdef __i386__
+#ifndef CONFIG_X86_32
+#define CONFIG_X86_32 1
+#endif
+#endif
+
+#ifdef __x86_64__
+#ifndef CONFIG_X86_64
+#define CONFIG_X86_64 1
+#endif
+#endif
+
+#if defined(__i386__) || defined (__x86_64__)
+#ifndef CONFIG_X86
+#define CONFIG_X86 1
+#endif
+#endif
+
+#ifdef __ia64__
+#ifndef CONFIG_IA64
+#define CONFIG_IA64 1
+#endif
+#endif
+
+#ifdef __PPC__
+#ifndef CONFIG_PPC
+#define CONFIG_PPC 1
+#endif
+#endif
+
+#ifdef __s390__
+#ifndef CONFIG_S390
+#define CONFIG_S390 1
+#endif
+#endif
+
+#endif
diff --git a/kvm/kernel/x86/Kbuild b/kvm/kernel/x86/Kbuild
new file mode 100644
index 000000000..c4723b1d7
--- /dev/null
+++ b/kvm/kernel/x86/Kbuild
@@ -0,0 +1,18 @@
+# trick to get the kvm-specific CONFIG_KVM_* definitions,
+# because the kernel source tree won't have them
+include $(obj)/../config.kbuild
+
+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 i8254.o coalesced_mmio.o irq_comm.o \
+ ../external-module-compat.o
+ifeq ($(EXT_CONFIG_KVM_TRACE),y)
+kvm-objs += kvm_trace.o
+endif
+ifeq ($(CONFIG_DMAR),y)
+kvm-objs += vtd.o
+endif
+kvm-intel-objs := vmx.o vmx-debug.o ../external-module-compat.o
+kvm-amd-objs := svm.o ../external-module-compat.o
+
+CFLAGS_kvm_main.o = -DKVM_MAIN
diff --git a/kvm/kernel/x86/Makefile.pre b/kvm/kernel/x86/Makefile.pre
new file mode 100644
index 000000000..e38baf134
--- /dev/null
+++ b/kvm/kernel/x86/Makefile.pre
@@ -0,0 +1 @@
+prerequisite:
diff --git a/kvm/kernel/x86/debug.h b/kvm/kernel/x86/debug.h
new file mode 100644
index 000000000..35793652a
--- /dev/null
+++ b/kvm/kernel/x86/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/x86/external-module-compat.h b/kvm/kernel/x86/external-module-compat.h
new file mode 100644
index 000000000..b6083ba64
--- /dev/null
+++ b/kvm/kernel/x86/external-module-compat.h
@@ -0,0 +1,390 @@
+
+/*
+ * Compatibility header for building as an external module.
+ */
+
+#include <linux/compiler.h>
+#include <linux/version.h>
+
+#include "../external-module-compat-comm.h"
+
+#include <asm/msr.h>
+
+#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
+
+#ifndef MSR_VM_CR
+#define MSR_VM_CR 0xc0010114
+#endif
+
+#ifndef MSR_VM_HSAVE_PA
+#define MSR_VM_HSAVE_PA 0xc0010117
+#endif
+
+#ifndef _EFER_SVME
+#define _EFER_SVME 12
+#define EFER_SVME (1<<_EFER_SVME)
+#endif
+
+#include <asm/cpufeature.h>
+
+#ifndef X86_FEATURE_SVM
+#define X86_FEATURE_SVM (6*32+ 2) /* Secure virtual machine */
+#endif
+
+#include <linux/smp.h>
+
+#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.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 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
+/*
+ * Include sched|preempt.h before defining CONFIG_PREEMPT_NOTIFIERS to avoid
+ * a miscompile.
+ */
+#include <linux/sched.h>
+#include <linux/preempt.h>
+#define CONFIG_PREEMPT_NOTIFIERS
+#define CONFIG_PREEMPT_NOTIFIERS_COMPAT
+
+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 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 preempt_notifier_sys_init(void) {}
+static inline void preempt_notifier_sys_exit(void) {}
+
+#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
+
+/* 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
+
+struct kvm_desc_struct {
+ union {
+ struct { unsigned int a, b; };
+ struct {
+ u16 limit0;
+ u16 base0;
+ unsigned base1: 8, type: 4, s: 1, dpl: 2, p: 1;
+ unsigned limit: 4, avl: 1, l: 1, d: 1, g: 1, base2: 8;
+ };
+
+ };
+} __attribute__((packed));
+
+struct kvm_ldttss_desc64 {
+ u16 limit0;
+ u16 base0;
+ unsigned base1 : 8, type : 5, dpl : 2, p : 1;
+ unsigned limit1 : 4, zero0 : 3, g : 1, base2 : 8;
+ u32 base3;
+ u32 zero1;
+} __attribute__((packed));
+
+struct kvm_desc_ptr {
+ unsigned short size;
+ unsigned long address;
+} __attribute__((packed));
+
+#include <asm/msr.h>
+#ifndef MSR_FS_BASE
+#define MSR_FS_BASE 0xc0000100
+#endif
+#ifndef MSR_GS_BASE
+#define MSR_GS_BASE 0xc0000101
+#endif
+
+/* undefine lapic */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+
+#undef lapic
+
+#endif
+
+#include <asm/hw_irq.h>
+#ifndef NMI_VECTOR
+#define NMI_VECTOR 2
+#endif
+
+#ifndef MSR_MTRRcap
+#define MSR_MTRRcap 0x0fe
+#define MSR_MTRRfix64K_00000 0x250
+#define MSR_MTRRfix16K_80000 0x258
+#define MSR_MTRRfix16K_A0000 0x259
+#define MSR_MTRRfix4K_C0000 0x268
+#define MSR_MTRRfix4K_C8000 0x269
+#define MSR_MTRRfix4K_D0000 0x26a
+#define MSR_MTRRfix4K_D8000 0x26b
+#define MSR_MTRRfix4K_E0000 0x26c
+#define MSR_MTRRfix4K_E8000 0x26d
+#define MSR_MTRRfix4K_F0000 0x26e
+#define MSR_MTRRfix4K_F8000 0x26f
+#define MSR_MTRRdefType 0x2ff
+#endif
+
+#ifndef MSR_IA32_CR_PAT
+#define MSR_IA32_CR_PAT 0x00000277
+#endif
+
+/* Define DEBUGCTLMSR bits */
+#ifndef DEBUGCTLMSR_LBR
+
+#define _DEBUGCTLMSR_LBR 0 /* last branch recording */
+#define _DEBUGCTLMSR_BTF 1 /* single-step on branches */
+
+#define DEBUGCTLMSR_LBR (1UL << _DEBUGCTLMSR_LBR)
+#define DEBUGCTLMSR_BTF (1UL << _DEBUGCTLMSR_BTF)
+
+#endif
+
+#include <asm/asm.h>
+
+#ifndef __ASM_SIZE
+# define ____ASM_FORM(x) " " #x " "
+# ifdef CONFIG_X86_64
+# define __ASM_SIZE(inst) ____ASM_FORM(inst##q)
+# else
+# define __ASM_SIZE(inst) ____ASM_FORM(inst##l)
+# endif
+#endif
+
+#ifndef _ASM_PTR
+# ifdef CONFIG_X86_64
+# define _ASM_PTR ".quad"
+# else
+# define _ASM_PTR ".long"
+# endif
+#endif
+
+/* Intel VT MSRs */
+#ifndef MSR_IA32_VMX_BASIC
+#define MSR_IA32_VMX_BASIC 0x00000480
+#define MSR_IA32_VMX_PINBASED_CTLS 0x00000481
+#define MSR_IA32_VMX_PROCBASED_CTLS 0x00000482
+#define MSR_IA32_VMX_EXIT_CTLS 0x00000483
+#define MSR_IA32_VMX_ENTRY_CTLS 0x00000484
+#define MSR_IA32_VMX_MISC 0x00000485
+#define MSR_IA32_VMX_CR0_FIXED0 0x00000486
+#define MSR_IA32_VMX_CR0_FIXED1 0x00000487
+#define MSR_IA32_VMX_CR4_FIXED0 0x00000488
+#define MSR_IA32_VMX_CR4_FIXED1 0x00000489
+#define MSR_IA32_VMX_VMCS_ENUM 0x0000048a
+#define MSR_IA32_VMX_PROCBASED_CTLS2 0x0000048b
+#define MSR_IA32_VMX_EPT_VPID_CAP 0x0000048c
+#endif
+
+#ifndef MSR_IA32_FEATURE_CONTROL
+#define MSR_IA32_FEATURE_CONTROL 0x0000003a
+
+#define FEATURE_CONTROL_LOCKED (1<<0)
+#define FEATURE_CONTROL_VMXON_ENABLED (1<<2)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) && defined(__x86_64__)
+
+#undef set_debugreg
+#define set_debugreg(value, register) \
+ __asm__("movq %0,%%db" #register \
+ : /* no output */ \
+ :"r" ((unsigned long)value))
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+
+struct mtrr_var_range {
+ u32 base_lo;
+ u32 base_hi;
+ u32 mask_lo;
+ u32 mask_hi;
+};
+
+/* In the Intel processor's MTRR interface, the MTRR type is always held in
+ an 8 bit field: */
+typedef u8 mtrr_type;
+
+#define MTRR_NUM_FIXED_RANGES 88
+#define MTRR_MAX_VAR_RANGES 256
+
+struct mtrr_state_type {
+ struct mtrr_var_range var_ranges[MTRR_MAX_VAR_RANGES];
+ mtrr_type fixed_ranges[MTRR_NUM_FIXED_RANGES];
+ unsigned char enabled;
+ unsigned char have_fixed;
+ mtrr_type def_type;
+};
+
+#endif
+
diff --git a/kvm/kernel/x86/hack-module.awk b/kvm/kernel/x86/hack-module.awk
new file mode 100644
index 000000000..f40c97276
--- /dev/null
+++ b/kvm/kernel/x86/hack-module.awk
@@ -0,0 +1,96 @@
+BEGIN { split("INIT_WORK tsc_khz desc_struct ldttss_desc64 desc_ptr " \
+ "on_each_cpu relay_open request_irq" , compat_apis); }
+
+/^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 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 = "mmget(&kvm->mm->mm_count);" }
+
+/^\t\.fault = / {
+ fcn = gensub(/,/, "", "g", $3)
+ $0 = "\t.VMA_OPS_FAULT(fault) = VMA_OPS_FAULT_FUNC(" fcn "),"
+}
+
+/^static int (.*_stat_get|lost_records_get)/ {
+ $3 = "__" $3
+}
+
+/DEFINE_SIMPLE_ATTRIBUTE.*(_stat_get|lost_records_get)/ {
+ name = gensub(/,/, "", "g", $2);
+ print "MAKE_SIMPLE_ATTRIBUTE_GETTER(" name ")"
+}
+
+{ sub(/linux\/mm_types\.h/, "linux/mm.h") }
+
+{ sub(/\<__user\>/, " ") }
+
+/^\t\.name = "kvm"/ { $0 = "\tset_kset_name(\"kvm\")," }
+
+/#include <linux\/compiler.h>/ { $0 = "" }
+/#include <linux\/clocksource.h>/ { $0 = "" }
+
+{ sub(/\<hrtimer_init\>/, "hrtimer_init_p") }
+{ sub(/\<hrtimer_start\>/, "hrtimer_start_p") }
+{ sub(/\<hrtimer_cancel\>/, "hrtimer_cancel_p") }
+
+/case KVM_CAP_SYNC_MMU/ { $0 = "#ifdef CONFIG_MMU_NOTIFIER\n" $0 "\n#endif" }
+
+{
+ for (i in compat_apis) {
+ ident = compat_apis[i]
+ sub("\\<" ident "\\>", "kvm_" ident)
+ }
+}
+
+/\kvm_.*_fops\.owner = module;/ { $0 = "IF_ANON_INODES_DOES_REFCOUNTS(" $0 ")" }
+
+{ print }
+
+/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);"
+}
+
+/\tkvm_init_debug/ {
+ print "\thrtimer_kallsyms_resolve();"
+}
+/apic->timer.dev.function =/ {
+ print "\thrtimer_data_pointer(&apic->timer.dev);"
+}
+/pt->timer.function =/ {
+ print "\thrtimer_data_pointer(&pt->timer);"
+}
diff --git a/kvm/kernel/x86/preempt.c b/kvm/kernel/x86/preempt.c
new file mode 100644
index 000000000..31128796a
--- /dev/null
+++ b/kvm/kernel/x86/preempt.c
@@ -0,0 +1,253 @@
+
+#ifdef CONFIG_PREEMPT_NOTIFIERS_COMPAT
+
+#include <linux/sched.h>
+#include <linux/percpu.h>
+
+static DEFINE_SPINLOCK(pn_lock);
+static LIST_HEAD(pn_list);
+
+#define dprintk(fmt) do { \
+ if (0) \
+ printk("%s (%d/%d): " fmt, __FUNCTION__, \
+ current->pid, raw_smp_processor_id()); \
+ } while (0)
+
+#if !defined(CONFIG_X86_64) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
+#define debugreg(x) debugreg[x]
+#else
+#define debugreg(x) debugreg##x
+#endif
+
+static void preempt_enable_sched_out_notifiers(void)
+{
+ asm volatile ("mov %0, %%db0" : : "r"(schedule));
+ asm volatile ("mov %0, %%db7" : : "r"(0x701ul));
+ current->thread.debugreg(7) = 0ul;
+#ifdef TIF_DEBUG
+ clear_tsk_thread_flag(current, TIF_DEBUG);
+#endif
+}
+
+static void preempt_enable_sched_in_notifiers(void * addr)
+{
+ asm volatile ("mov %0, %%db0" : : "r"(addr));
+ asm volatile ("mov %0, %%db7" : : "r"(0x701ul));
+ current->thread.debugreg(0) = (unsigned long) addr;
+ current->thread.debugreg(7) = 0x701ul;
+#ifdef TIF_DEBUG
+ set_tsk_thread_flag(current, TIF_DEBUG);
+#endif
+}
+
+static void __preempt_disable_notifiers(void)
+{
+ asm volatile ("mov %0, %%db7" : : "r"(0ul));
+}
+
+static void preempt_disable_notifiers(void)
+{
+ __preempt_disable_notifiers();
+ current->thread.debugreg(7) = 0ul;
+#ifdef TIF_DEBUG
+ clear_tsk_thread_flag(current, TIF_DEBUG);
+#endif
+}
+
+static void fastcall __attribute__((used)) preempt_notifier_trigger(void *** ip)
+{
+ struct preempt_notifier *pn;
+ int cpu = raw_smp_processor_id();
+ int found = 0;
+
+ dprintk(" - in\n");
+ //dump_stack();
+ spin_lock(&pn_lock);
+ list_for_each_entry(pn, &pn_list, link)
+ if (pn->tsk == current) {
+ found = 1;
+ break;
+ }
+ spin_unlock(&pn_lock);
+
+ if (found) {
+ if ((void *) *ip != schedule) {
+ dprintk("sched_in\n");
+ preempt_enable_sched_out_notifiers();
+
+ preempt_disable();
+ local_irq_enable();
+ pn->ops->sched_in(pn, cpu);
+ local_irq_disable();
+ preempt_enable_no_resched();
+ } else {
+ void * sched_in_addr;
+ dprintk("sched_out\n");
+#ifdef CONFIG_X86_64
+ sched_in_addr = **(ip+3);
+#else
+ /* no special debug stack switch on x86 */
+ sched_in_addr = (void *) *(ip+3);
+#endif
+ preempt_enable_sched_in_notifiers(sched_in_addr);
+
+ preempt_disable();
+ local_irq_enable();
+ pn->ops->sched_out(pn, NULL);
+ local_irq_disable();
+ preempt_enable_no_resched();
+ }
+ } else
+ __preempt_disable_notifiers();
+ 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 %db7, " TMP " \n\t"
+ "cmp $0x701, " TMP " \n\t"
+ "pop " TMP " \n\t"
+ "jnz .Lnotme \n\t"
+ "push " TMP " \n\t"
+ "mov %db6, " TMP " \n\t"
+ "test $0x1, " TMP " \n\t"
+ "pop " TMP " \n\t"
+ "jz .Lnotme \n\t"
+ SAVE_REGS "\n\t"
+#ifdef CONFIG_X86_64
+ "leaq 120(%rsp),%rdi\n\t"
+#else
+ "leal 32(%esp),%eax\n\t"
+#endif
+ "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 preempt_notifier_register(struct preempt_notifier *notifier)
+{
+ unsigned long flags;
+
+ dprintk(" - in\n");
+ spin_lock_irqsave(&pn_lock, flags);
+ preempt_enable_sched_out_notifiers();
+ notifier->tsk = current;
+ list_add(&notifier->link, &pn_list);
+ spin_unlock_irqrestore(&pn_lock, flags);
+ dprintk(" - out\n");
+}
+
+void preempt_notifier_unregister(struct preempt_notifier *notifier)
+{
+ unsigned long flags;
+
+ dprintk(" - in\n");
+ spin_lock_irqsave(&pn_lock, flags);
+ list_del(&notifier->link);
+ spin_unlock_irqrestore(&pn_lock, flags);
+ preempt_disable_notifiers();
+ 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)
+{
+#ifdef TIF_DEBUG
+ if (!test_tsk_thread_flag(current, TIF_DEBUG))
+#else
+ if (!current->thread.debugreg(7))
+#endif
+ __preempt_disable_notifiers();
+}
+
+void preempt_notifier_sys_exit(void)
+{
+ struct idt_desc idt_desc;
+
+ dprintk("\n");
+ kvm_on_each_cpu(do_disable, NULL, 1);
+ asm ("sidt %0" : "=m"(idt_desc));
+ idt_desc.gates[1] = orig_int1_gate;
+}
+
+#endif
diff --git a/kvm/kernel/x86/vmx-debug.c b/kvm/kernel/x86/vmx-debug.c
new file mode 100644
index 000000000..29316a0e5
--- /dev/null
+++ b/kvm/kernel/x86/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/kvm b/kvm/kvm
new file mode 100755
index 000000000..cb9ecf83b
--- /dev/null
+++ b/kvm/kvm
@@ -0,0 +1,283 @@
+#!/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')
+
+arch = config.get('shell', 'arch')
+p = re.compile("^i\d86$")
+if len(p.findall(arch)):
+ arch = 'x86_64'
+if arch != 'x86_64' and arch != 'ia64':
+ raise Exception('unsupported architecture %s' % arch)
+
+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('--nictype',
+ help = 'use this specific nic type (vendor)',
+ dest = 'nictype',
+ default = 'rtl8139',
+ )
+
+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 arch == 'x86_64':
+ archdir = 'x86'
+ elif arch == 'ia64':
+ archdir = 'ia64'
+ if os.spawnl(os.P_WAIT, '/sbin/insmod', 'insmod',
+ 'kernel/' + archdir + '/%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'
+
+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=%s' % (mac,options.nictype,),
+ '-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..92acb0ed2
--- /dev/null
+++ b/kvm/kvm.spec
@@ -0,0 +1,139 @@
+Name: kvm
+Version: 0.0
+Release: 0
+Summary: Kernel Virtual Machine virtualization environment
+
+Group: System Environment/Kernel
+License: GPL
+URL: http://www.qumranet.com
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
+
+ExclusiveArch: i386 x86_64 ia64
+
+Requires: kvm-kmod bridge-utils
+
+%define Distribution %(rpm -q -qf /etc/redhat-release --qf '%%{name}' | cut -d"-" -f 1)
+%define os_version %(rpm -q --qf '%%{version}' %{Distribution}-release)
+%define os_release %(rpm -q --qf '%%{release}' %{Distribution}-release | cut -d"." -f 1)
+
+%if %([ x"%{Distribution}" = x"fedora" -a x"%{os_version}" = x"5" ] && echo 1 || echo 0)
+%define require_gccver 32
+%endif
+
+%if %([ x"%{Distribution}" = x"fedora" -a 0"%{os_version}" -ge "8" ] && echo 1 || echo 0)
+%define qemuldflags --qemu-ldflags=-Wl,--build-id
+%else
+%define qemuldflags ""
+%endif
+
+%if %([ x"%{Distribution}" = x"centos" -a x"%{os_version}" = x"4" ] && echo 1 || echo 0)
+%define require_gccver 32
+%endif
+
+%if %([ x"%{Distribution}" = x"redhat" -a x"%{os_release}" = x"5" ] && echo 1 || echo 0)
+%define require_gccver 34
+%endif
+
+%if %( [ x"%{require_gccver}" = x"32" ] && echo 1 || echo 0)
+BuildRequires: compat-gcc-32
+%else
+BuildRequires: compat-gcc-34
+%endif
+
+BuildRequires: SDL-devel zlib-devel alsa-lib-devel
+
+%define _prebuilt %{?prebuilt:1}%{!?prebuilt:0}
+
+%if !%{_prebuilt}
+Source0: kvm.tar.gz
+Source1: user.tar.gz
+Source2: kernel.tar.gz
+Source3: scripts.tar.gz
+Source4: Makefile
+Source5: configure
+Source6: kvm_stat
+Source7: libkvm.tar.gz
+Source8: extboot.tar.gz
+%endif
+
+%description
+The Kernel Virtual Machine provides a virtualization enviroment for processors
+with hardware support for virtualization: Intel's VT-x&VT-i and AMD's AMD-V.
+
+%prep
+
+%if !%{_prebuilt}
+%setup -T -b 0 -n qemu
+%setup -T -b 1 -n user -D
+%setup -T -b 2 -n kernel -D
+%setup -T -b 7 -n libkvm -D
+%setup -T -b 3 -n scripts -D
+%setup -T -b 8 -n extboot -D
+cd ..
+cp %{_sourcedir}/Makefile %{_sourcedir}/configure %{_sourcedir}/kvm_stat .
+%endif
+
+%build
+
+rm -rf %{buildroot}
+
+%if !%{_prebuilt}
+cd ..
+./configure --prefix=/usr/kvm %{qemuldflags}
+make -C libkvm
+make -C user
+%ifarch i386 x86_64
+make extboot
+%endif
+#(cd qemu;
+# ./co
+# kpath="$(readlink -f ../kernel/include)"
+# upath="$(readlink -f ../user)"
+# ./configure --target-list=$(uname -i)-softmmu \
+# --extra-cflags="-I$kpath -I$upath" \
+# --extra-ldflags="-L$upath" \
+# --disable-kqemu --enable-kvm --prefix=/usr/kvm
+#)
+make -C qemu
+%endif
+
+%install
+
+%if !%{_prebuilt}
+cd ..
+%else
+cd %{objdir}
+%endif
+
+make DESTDIR=%{buildroot} install-rpm
+
+%define bindir /usr/bin
+%define bin %{bindir}/kvm
+%define initdir /etc/init.d
+%define confdir /etc/kvm
+%define utilsdir /etc/kvm/utils
+
+%post
+/sbin/chkconfig --add kvm
+/sbin/chkconfig --level 2345 kvm on
+/sbin/chkconfig --level 16 kvm off
+/usr/sbin/groupadd -fg 444 kvm
+
+%preun
+if [ "$1" != 0 ]; then
+ /sbin/service kvm stop
+ /sbin/chkconfig --level 2345 kvm off
+ /sbin/chkconfig --del kvm
+fi
+
+%clean
+%{__rm} -rf %{buildroot}
+
+%files
+/usr/bin/kvm
+/usr/bin/kvm_stat
+%{confdir}/qemu-ifup
+%{initdir}/kvm
+/etc/udev/rules.d/*kvm*.rules
+/usr/kvm
+%changelog
diff --git a/kvm/kvm_stat b/kvm/kvm_stat
new file mode 100755
index 000000000..21aff5b68
--- /dev/null
+++ b/kvm/kvm_stat
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+import curses
+import sys, os, time, optparse
+
+class Stats:
+ def __init__(self, fields = None):
+ def wanted(key):
+ import re
+ if not fields:
+ return True
+ return re.match(fields, key) != None
+ self.base = '/sys/kernel/debug/kvm'
+ self.values = {}
+ for key in os.listdir(self.base):
+ if wanted(key):
+ self.values[key] = None
+ def get(self):
+ for key, oldval in self.values.iteritems():
+ newval = int(file(self.base + '/' + key).read())
+ newdelta = None
+ if oldval is not None:
+ newdelta = newval - oldval[0]
+ self.values[key] = (newval, newdelta)
+ return self.values
+
+if not os.access('/sys/kernel/debug', os.F_OK):
+ print 'Please enable CONFIG_DEBUG_FS in your kernel'
+ sys.exit(1)
+if not os.access('/sys/kernel/debug/kvm', os.F_OK):
+ print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
+ print "and ensure the kvm modules are loaded"
+ sys.exit(1)
+
+label_width = 20
+number_width = 10
+
+def tui(screen, stats):
+ curses.use_default_colors()
+ curses.noecho()
+ def refresh():
+ screen.erase()
+ screen.addstr(0, 0, 'kvm statistics')
+ row = 2
+ s = stats.get()
+ for key in sorted(s.keys()):
+ if row >= screen.getmaxyx()[0]:
+ break
+ values = s[key]
+ col = 1
+ screen.addstr(row, col, key)
+ col += label_width
+ screen.addstr(row, col, '%10d' % (values[0],))
+ col += number_width
+ if values[1] is not None:
+ screen.addstr(row, col, '%8d' % (values[1],))
+ row += 1
+ screen.refresh()
+
+ while True:
+ refresh()
+ curses.halfdelay(10)
+ try:
+ c = screen.getkey()
+ if c == 'q':
+ break
+ except KeyboardInterrupt:
+ break
+ except curses.error:
+ continue
+
+def batch(stats):
+ s = stats.get()
+ time.sleep(1)
+ s = stats.get()
+ for key in sorted(s.keys()):
+ values = s[key]
+ print '%-22s%10d%10d' % (key, values[0], values[1])
+
+def log(stats):
+ keys = sorted(stats.get().iterkeys())
+ def banner():
+ for k in keys:
+ print '%10s' % k[0:9],
+ print
+ def statline():
+ s = stats.get()
+ for k in keys:
+ print ' %9d' % s[k][1],
+ print
+ line = 0
+ banner_repeat = 20
+ while True:
+ time.sleep(1)
+ if line % banner_repeat == 0:
+ banner()
+ statline()
+ line += 1
+
+options = optparse.OptionParser()
+options.add_option('-1', '--once', '--batch',
+ action = 'store_true',
+ default = False,
+ dest = 'once',
+ help = 'run in batch mode for one second',
+ )
+options.add_option('-l', '--log',
+ action = 'store_true',
+ default = False,
+ dest = 'log',
+ help = 'run in logging mode (like vmstat)',
+ )
+options.add_option('-f', '--fields',
+ action = 'store',
+ default = None,
+ dest = 'fields',
+ help = 'fields to display (regex)',
+ )
+(options, args) = options.parse_args(sys.argv)
+
+stats = Stats(fields = options.fields)
+
+if options.log:
+ log(stats)
+elif not options.once:
+ import curses.wrapper
+ curses.wrapper(tui, stats)
+else:
+ batch(stats)
diff --git a/kvm/libfdt/Makefile b/kvm/libfdt/Makefile
new file mode 100644
index 000000000..db80e47a6
--- /dev/null
+++ b/kvm/libfdt/Makefile
@@ -0,0 +1,19 @@
+include ../config.mak
+include ../user/config.mak
+
+LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c
+LIBFDT_INCLUDES = fdt.h libfdt.h
+LIBFDT_EXTRA = libfdt_internal.h
+LIBFDT_LIB = libfdt.a
+
+LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
+
+CFLAGS += -I .
+
+$(LIBFDT_LIB): $(LIBFDT_OBJS)
+ $(AR) rcs $@ $^
+
+all: $(LIBFDT_LIB)
+
+clean:
+ rm -rf *.o *.a
diff --git a/kvm/libfdt/README b/kvm/libfdt/README
new file mode 100644
index 000000000..491bc76ee
--- /dev/null
+++ b/kvm/libfdt/README
@@ -0,0 +1,3 @@
+libfdt was grabbed from dtc source. This is the upstream source for libfdt.
+It can be found here:
+http://www.jdl.com/software/
diff --git a/kvm/libfdt/fdt.c b/kvm/libfdt/fdt.c
new file mode 100644
index 000000000..bd9171237
--- /dev/null
+++ b/kvm/libfdt/fdt.c
@@ -0,0 +1,194 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_check_header(const void *fdt)
+{
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ /* Complete tree */
+ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ } else if (fdt_magic(fdt) == SW_MAGIC) {
+ /* Unfinished sequential-write blob */
+ if (fdt_size_dt_struct(fdt) == 0)
+ return -FDT_ERR_BADSTATE;
+ } else {
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ return 0;
+}
+
+const void *fdt_offset_ptr(const void *fdt, int offset, int len)
+{
+ const void *p;
+
+ if (fdt_version(fdt) >= 0x11)
+ if (((offset + len) < offset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ p = _fdt_offset_ptr(fdt, offset);
+
+ if (p + len < p)
+ return NULL;
+ return p;
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset)
+{
+ const uint32_t *tagp, *lenp;
+ uint32_t tag;
+ const char *p;
+
+ if (offset % FDT_TAGSIZE)
+ return -1;
+
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (! tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /* skip name */
+ do {
+ p = fdt_offset_ptr(fdt, offset++, 1);
+ } while (p && (*p != '\0'));
+ if (! p)
+ return FDT_END;
+ break;
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (! lenp)
+ return FDT_END;
+ /* skip name offset, length and value */
+ offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
+ break;
+ }
+
+ if (nextoffset)
+ *nextoffset = ALIGN(offset, FDT_TAGSIZE);
+
+ return tag;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset >= 0) {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ case FDT_BEGIN_NODE:
+ if (depth)
+ (*depth)++;
+ break;
+
+ case FDT_END_NODE:
+ if (depth)
+ (*depth)--;
+ break;
+
+ case FDT_END:
+ return -FDT_ERR_NOTFOUND;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_BEGIN_NODE);
+
+ return offset;
+}
+
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
+{
+ int len = strlen(s) + 1;
+ const char *last = strtab + tabsize - len;
+ const char *p;
+
+ for (p = strtab; p <= last; p++)
+ if (memeq(p, s, len))
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ CHECK_HEADER(fdt);
+
+ if (fdt_totalsize(fdt) > bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ memmove(buf, fdt, fdt_totalsize(fdt));
+ return 0;
+}
diff --git a/kvm/libfdt/fdt.h b/kvm/libfdt/fdt.h
new file mode 100644
index 000000000..48ccfd910
--- /dev/null
+++ b/kvm/libfdt/fdt.h
@@ -0,0 +1,60 @@
+#ifndef _FDT_H
+#define _FDT_H
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+ uint32_t magic; /* magic word FDT_MAGIC */
+ uint32_t totalsize; /* total size of DT block */
+ uint32_t off_dt_struct; /* offset to structure */
+ uint32_t off_dt_strings; /* offset to strings */
+ uint32_t off_mem_rsvmap; /* offset to memory reserve map */
+ uint32_t version; /* format version */
+ uint32_t last_comp_version; /* last compatible version */
+
+ /* version 2 fields below */
+ uint32_t boot_cpuid_phys; /* Which physical CPU id we're
+ booting on */
+ /* version 3 fields below */
+ uint32_t size_dt_strings; /* size of the strings block */
+
+ /* version 17 fields below */
+ uint32_t size_dt_struct; /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+ uint64_t address;
+ uint64_t size;
+};
+
+struct fdt_node_header {
+ uint32_t tag;
+ char name[0];
+};
+
+struct fdt_property {
+ uint32_t tag;
+ uint32_t len;
+ uint32_t nameoff;
+ char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_TAGSIZE sizeof(uint32_t)
+
+#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
+#define FDT_END_NODE 0x2 /* End node */
+#define FDT_PROP 0x3 /* Property: name off,
+ size, content */
+#define FDT_NOP 0x4 /* nop */
+#define FDT_END 0x9
+
+#define FDT_V1_SIZE (7*sizeof(uint32_t))
+#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
+#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
+#define FDT_V16_SIZE FDT_V3_SIZE
+#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t))
+
+#endif /* _FDT_H */
diff --git a/kvm/libfdt/fdt_ro.c b/kvm/libfdt/fdt_ro.c
new file mode 100644
index 000000000..63fa1290b
--- /dev/null
+++ b/kvm/libfdt/fdt_ro.c
@@ -0,0 +1,476 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int nodename_eq(const void *fdt, int offset,
+ const char *s, int len)
+{
+ const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
+
+ if (! p)
+ /* short match */
+ return 0;
+
+ if (memcmp(p, s, len) != 0)
+ return 0;
+
+ if (p[len] == '\0')
+ return 1;
+ else if (!memchr(s, '@', len) && (p[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const char *fdt_string(const void *fdt, int stroffset)
+{
+ return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ CHECK_HEADER(fdt);
+ *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
+ *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
+ return 0;
+}
+
+int fdt_num_mem_rsv(const void *fdt)
+{
+ int i = 0;
+
+ while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
+ i++;
+ return i;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int offset,
+ const char *name, int namelen)
+{
+ int depth;
+
+ CHECK_HEADER(fdt);
+
+ for (depth = 0;
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth < 0)
+ return -FDT_ERR_NOTFOUND;
+ else if ((depth == 1)
+ && nodename_eq(fdt, offset, name, namelen))
+ return offset;
+ }
+
+ return offset; /* error */
+}
+
+int fdt_subnode_offset(const void *fdt, int parentoffset,
+ const char *name)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ const char *end = path + strlen(path);
+ const char *p = path;
+ int offset = 0;
+
+ CHECK_HEADER(fdt);
+
+ if (*path != '/')
+ return -FDT_ERR_BADPATH;
+
+ while (*p) {
+ const char *q;
+
+ while (*p == '/')
+ p++;
+ if (! *p)
+ return offset;
+ q = strchr(p, '/');
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh;
+ int err;
+
+ if ((err = fdt_check_header(fdt)) != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
+ if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
+ goto fail;
+
+ if (len)
+ *len = strlen(nh->name);
+
+ return nh->name;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ uint32_t tag;
+ const struct fdt_property *prop;
+ int namestroff;
+ int offset, nextoffset;
+ int err;
+
+ if ((err = fdt_check_header(fdt)) != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ if (nodeoffset % FDT_TAGSIZE)
+ goto fail;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ goto fail;
+
+ do {
+ offset = nextoffset;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_END:
+ err = -FDT_ERR_TRUNCATED;
+ goto fail;
+
+ case FDT_BEGIN_NODE:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ case FDT_PROP:
+ err = -FDT_ERR_BADSTRUCTURE;
+ prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
+ if (! prop)
+ goto fail;
+ namestroff = fdt32_to_cpu(prop->nameoff);
+ if (streq(fdt_string(fdt, namestroff), name)) {
+ /* Found it! */
+ int len = fdt32_to_cpu(prop->len);
+ prop = fdt_offset_ptr(fdt, offset,
+ sizeof(*prop)+len);
+ if (! prop)
+ goto fail;
+
+ if (lenp)
+ *lenp = len;
+
+ return prop;
+ }
+ break;
+
+ default:
+ err = -FDT_ERR_BADSTRUCTURE;
+ goto fail;
+ }
+ } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
+
+ err = -FDT_ERR_NOTFOUND;
+ fail:
+ if (lenp)
+ *lenp = err;
+ return NULL;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property(fdt, nodeoffset, name, lenp);
+ if (! prop)
+ return NULL;
+
+ return prop->data;
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const uint32_t *php;
+ int len;
+
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+
+ return fdt32_to_cpu(*php);
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ int pdepth = 0, p = 0;
+ int offset, depth, namelen;
+ const char *name;
+
+ CHECK_HEADER(fdt);
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (pdepth < depth)
+ continue; /* overflowed buffer */
+
+ while (pdepth > depth) {
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ pdepth--;
+ }
+
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
+
+ if (offset == nodeoffset) {
+ if (pdepth < (depth + 1))
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return p;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int offset, depth;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ CHECK_HEADER(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth == supernodedepth)
+ supernodeoffset = offset;
+
+ if (offset == nodeoffset) {
+ if (nodedepth)
+ *nodedepth = depth;
+
+ if (supernodedepth > depth)
+ return -FDT_ERR_NOTFOUND;
+ else
+ return supernodeoffset;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_depth(const void *fdt, int nodeoffset)
+{
+ int nodedepth;
+ int err;
+
+ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+ if (err)
+ return (err < 0) ? err : -FDT_ERR_INTERNAL;
+ return nodedepth;
+}
+
+int fdt_parent_offset(const void *fdt, int nodeoffset)
+{
+ int nodedepth = fdt_node_depth(fdt, nodeoffset);
+
+ if (nodedepth < 0)
+ return nodedepth;
+ return fdt_supernode_atdepth_offset(fdt, nodeoffset,
+ nodedepth - 1, NULL);
+}
+
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ int offset;
+ const void *val;
+ int len;
+
+ CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_getprop(), then if that didn't
+ * find what we want, we scan over them again making our way
+ * to the next node. Still it's the easiest to implement
+ * approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ if ((phandle == 0) || (phandle == -1))
+ return -FDT_ERR_BADPHANDLE;
+ phandle = cpu_to_fdt32(phandle);
+ return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
+ &phandle, sizeof(phandle));
+}
+
+int _stringlist_contains(const void *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const void *p;
+
+ while (listlen >= len) {
+ if (memcmp(str, strlist, len+1) == 0)
+ return 1;
+ p = memchr(strlist, '\0', listlen);
+ if (!p)
+ return 0; /* malformed strlist.. */
+ listlen -= (p-strlist) + 1;
+ strlist = p + 1;
+ }
+ return 0;
+}
+
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ const void *prop;
+ int len;
+
+ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+ if (!prop)
+ return len;
+ if (_stringlist_contains(prop, len, compatible))
+ return 0;
+ else
+ return 1;
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ int offset, err;
+
+ CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_node_check_compatible(), then if
+ * that didn't find what we want, we scan over them again
+ * making our way to the next node. Still it's the easiest to
+ * implement approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ err = fdt_node_check_compatible(fdt, offset, compatible);
+ if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
diff --git a/kvm/libfdt/fdt_rw.c b/kvm/libfdt/fdt_rw.c
new file mode 100644
index 000000000..0df472bc5
--- /dev/null
+++ b/kvm/libfdt/fdt_rw.c
@@ -0,0 +1,467 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int _blocks_misordered(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < ALIGN(sizeof(struct fdt_header), 8))
+ || (fdt_off_dt_struct(fdt) <
+ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
+ || (fdt_off_dt_strings(fdt) <
+ (fdt_off_dt_struct(fdt) + struct_size))
+ || (fdt_totalsize(fdt) <
+ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
+}
+
+static int rw_check_header(void *fdt)
+{
+ CHECK_HEADER(fdt);
+
+ if (fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_BADLAYOUT;
+ if (fdt_version(fdt) > 17)
+ fdt_set_version(fdt, 17);
+
+ return 0;
+}
+
+#define RW_CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = rw_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+static inline int _blob_data_size(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int _blob_splice(void *fdt, void *p, int oldlen, int newlen)
+{
+ void *end = fdt + _blob_data_size(fdt);
+
+ if (((p + oldlen) < p) || ((p + oldlen) > end))
+ return -FDT_ERR_BADOFFSET;
+ if ((end - oldlen + newlen) > (fdt + fdt_totalsize(fdt)))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, end - p - oldlen);
+ return 0;
+}
+
+static int _blob_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = _blob_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
+ if (err)
+ return err;
+ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _blob_splice_struct(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = _blob_splice(fdt, p, oldlen, newlen)))
+ return err;
+
+ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _blob_splice_string(void *fdt, int newlen)
+{
+ void *p = fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = _blob_splice(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+static int _find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
+ const char *p;
+ char *new;
+ int len = strlen(s) + 1;
+ int err;
+
+ p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
+ if (p)
+ /* found it */
+ return (p - strtab);
+
+ new = strtab + fdt_size_dt_strings(fdt);
+ err = _blob_splice_string(fdt, len);
+ if (err)
+ return err;
+
+ memcpy(new, s, len);
+ return (new - strtab);
+}
+
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
+ err = _blob_splice_mem_rsv(fdt, re, 0, 1);
+ if (err)
+ return err;
+
+ re->address = cpu_to_fdt64(address);
+ re->size = cpu_to_fdt64(size);
+ return 0;
+}
+
+int fdt_del_mem_rsv(void *fdt, int n)
+{
+ struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ err = _blob_splice_mem_rsv(fdt, re, 1, 0);
+ if (err)
+ return err;
+ return 0;
+}
+
+static int _resize_property(void *fdt, int nodeoffset, const char *name, int len,
+ struct fdt_property **prop)
+{
+ int oldlen;
+ int err;
+
+ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (! (*prop))
+ return oldlen;
+
+ if ((err = _blob_splice_struct(fdt, (*prop)->data,
+ ALIGN(oldlen, FDT_TAGSIZE),
+ ALIGN(len, FDT_TAGSIZE))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int _add_property(void *fdt, int nodeoffset, const char *name, int len,
+ struct fdt_property **prop)
+{
+ uint32_t tag;
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+
+ namestroff = _find_add_string(fdt, name);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = _fdt_offset_ptr_w(fdt, nextoffset);
+ proplen = sizeof(**prop) + ALIGN(len, FDT_TAGSIZE);
+
+ err = _blob_splice_struct(fdt, *prop, 0, proplen);
+ if (err)
+ return err;
+
+ (*prop)->tag = cpu_to_fdt32(FDT_PROP);
+ (*prop)->nameoff = cpu_to_fdt32(namestroff);
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+int fdt_set_name(void *fdt, int nodeoffset, const char *name)
+{
+ char *namep;
+ int oldlen, newlen;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ namep = (char *)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = _blob_splice_struct(fdt, namep, ALIGN(oldlen+1, FDT_TAGSIZE),
+ ALIGN(newlen+1, FDT_TAGSIZE));
+ if (err)
+ return err;
+
+ memcpy(namep, name, newlen+1);
+ return 0;
+}
+
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ err = _resize_property(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = _add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_delprop(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len, proplen;
+
+ RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ proplen = sizeof(*prop) + ALIGN(len, FDT_TAGSIZE);
+ return _blob_splice_struct(fdt, prop, proplen, 0);
+}
+
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ struct fdt_node_header *nh;
+ int offset, nextoffset;
+ int nodelen;
+ int err;
+ uint32_t tag;
+ uint32_t *endtag;
+
+ RW_CHECK_HEADER(fdt);
+
+ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+ if (offset >= 0)
+ return -FDT_ERR_EXISTS;
+ else if (offset != -FDT_ERR_NOTFOUND)
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ } while ((tag == FDT_PROP) || (tag == FDT_NOP));
+
+ nh = _fdt_offset_ptr_w(fdt, offset);
+ nodelen = sizeof(*nh) + ALIGN(namelen+1, FDT_TAGSIZE) + FDT_TAGSIZE;
+
+ err = _blob_splice_struct(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, ALIGN(namelen+1, FDT_TAGSIZE));
+ memcpy(nh->name, name, namelen);
+ endtag = (uint32_t *)((void *)nh + nodelen - FDT_TAGSIZE);
+ *endtag = cpu_to_fdt32(FDT_END_NODE);
+
+ return offset;
+}
+
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
+{
+ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_del_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ RW_CHECK_HEADER(fdt);
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return _blob_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void _packblocks(const void *fdt, void *buf,
+ int mem_rsv_size, int struct_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(buf + mem_rsv_off, fdt + fdt_off_mem_rsvmap(fdt), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(buf, mem_rsv_off);
+
+ memmove(buf + struct_off, fdt + fdt_off_dt_struct(fdt), struct_size);
+ fdt_set_off_dt_struct(buf, struct_off);
+ fdt_set_size_dt_struct(buf, struct_size);
+
+ memmove(buf + strings_off, fdt + fdt_off_dt_strings(fdt),
+ fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(buf, strings_off);
+ fdt_set_size_dt_strings(buf, fdt_size_dt_strings(fdt));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ void *tmp;
+
+ CHECK_HEADER(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+
+ if (fdt_version(fdt) >= 17) {
+ struct_size = fdt_size_dt_struct(fdt);
+ } else {
+ struct_size = 0;
+ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+ ;
+ }
+
+ if (!_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
+ /* no further work necessary */
+ err = fdt_move(fdt, buf, bufsize);
+ if (err)
+ return err;
+ fdt_set_version(buf, 17);
+ fdt_set_size_dt_struct(buf, struct_size);
+ fdt_set_totalsize(buf, bufsize);
+ return 0;
+ }
+
+ /* Need to reorder */
+ newsize = ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ if (((buf + newsize) <= fdt)
+ || (buf >= (fdt + fdt_totalsize(fdt)))) {
+ tmp = buf;
+ } else {
+ tmp = (void *)fdt + fdt_totalsize(fdt);
+ if ((tmp + newsize) > (buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ _packblocks(fdt, tmp, mem_rsv_size, struct_size);
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+ fdt_set_totalsize(buf, bufsize);
+ fdt_set_version(buf, 17);
+ fdt_set_last_comp_version(buf, 16);
+ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
+
+ return 0;
+}
+
+int fdt_pack(void *fdt)
+{
+ int mem_rsv_size;
+ int err;
+
+ err = rw_check_header(fdt);
+ if (err)
+ return err;
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ _packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
+ fdt_set_totalsize(fdt, _blob_data_size(fdt));
+
+ return 0;
+}
diff --git a/kvm/libfdt/fdt_strerror.c b/kvm/libfdt/fdt_strerror.c
new file mode 100644
index 000000000..f9d32ef53
--- /dev/null
+++ b/kvm/libfdt/fdt_strerror.c
@@ -0,0 +1,96 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+struct errtabent {
+ const char *str;
+};
+
+#define ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct errtabent errtable[] = {
+ ERRTABENT(FDT_ERR_NOTFOUND),
+ ERRTABENT(FDT_ERR_EXISTS),
+ ERRTABENT(FDT_ERR_NOSPACE),
+
+ ERRTABENT(FDT_ERR_BADOFFSET),
+ ERRTABENT(FDT_ERR_BADPATH),
+ ERRTABENT(FDT_ERR_BADSTATE),
+
+ ERRTABENT(FDT_ERR_TRUNCATED),
+ ERRTABENT(FDT_ERR_BADMAGIC),
+ ERRTABENT(FDT_ERR_BADVERSION),
+ ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ ERRTABENT(FDT_ERR_BADLAYOUT),
+};
+#define ERRTABSIZE (sizeof(errtable) / sizeof(errtable[0]))
+
+const char *fdt_strerror(int errval)
+{
+ if (errval > 0)
+ return "<valid offset/length>";
+ else if (errval == 0)
+ return "<no error>";
+ else if (errval > -ERRTABSIZE) {
+ const char *s = errtable[-errval].str;
+
+ if (s)
+ return s;
+ }
+
+ return "<unknown error>";
+}
diff --git a/kvm/libfdt/fdt_sw.c b/kvm/libfdt/fdt_sw.c
new file mode 100644
index 000000000..dda2de34b
--- /dev/null
+++ b/kvm/libfdt/fdt_sw.c
@@ -0,0 +1,258 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int check_header_sw(void *fdt)
+{
+ if (fdt_magic(fdt) != SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ return 0;
+}
+
+static void *grab_space(void *fdt, int len)
+{
+ int offset = fdt_size_dt_struct(fdt);
+ int spaceleft;
+
+ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
+ - fdt_size_dt_strings(fdt);
+
+ if ((offset + len < offset) || (offset + len > spaceleft))
+ return NULL;
+
+ fdt_set_size_dt_struct(fdt, offset + len);
+ return fdt_offset_ptr_w(fdt, offset, len);
+}
+
+int fdt_create(void *buf, int bufsize)
+{
+ void *fdt = buf;
+
+ if (bufsize < sizeof(struct fdt_header))
+ return -FDT_ERR_NOSPACE;
+
+ memset(buf, 0, bufsize);
+
+ fdt_set_magic(fdt, SW_MAGIC);
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+ fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
+ fdt_set_totalsize(fdt, bufsize);
+
+ fdt_set_off_mem_rsvmap(fdt, ALIGN(sizeof(struct fdt_header),
+ sizeof(struct fdt_reserve_entry)));
+ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
+ fdt_set_off_dt_strings(fdt, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err = check_header_sw(fdt);
+ int offset;
+
+ if (err)
+ return err;
+ if (fdt_size_dt_struct(fdt))
+ return -FDT_ERR_BADSTATE;
+
+ offset = fdt_off_dt_struct(fdt);
+ if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+
+ re = (struct fdt_reserve_entry *)(fdt + offset);
+ re->address = cpu_to_fdt64(addr);
+ re->size = cpu_to_fdt64(size);
+
+ fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
+
+ return 0;
+}
+
+int fdt_finish_reservemap(void *fdt)
+{
+ return fdt_add_reservemap_entry(fdt, 0, 0);
+}
+
+int fdt_begin_node(void *fdt, const char *name)
+{
+ struct fdt_node_header *nh;
+ int err = check_header_sw(fdt);
+ int namelen = strlen(name) + 1;
+
+ if (err)
+ return err;
+
+ nh = grab_space(fdt, sizeof(*nh) + ALIGN(namelen, FDT_TAGSIZE));
+ if (! nh)
+ return -FDT_ERR_NOSPACE;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memcpy(nh->name, name, namelen);
+ return 0;
+}
+
+int fdt_end_node(void *fdt)
+{
+ uint32_t *en;
+ int err = check_header_sw(fdt);
+
+ if (err)
+ return err;
+
+ en = grab_space(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ const char *p;
+ int strtabsize = fdt_size_dt_strings(fdt);
+ int len = strlen(s) + 1;
+ int struct_top, offset;
+
+ p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
+ if (p)
+ return p - strtab;
+
+ /* Add it */
+ offset = -strtabsize - len;
+ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ if (fdt_totalsize(fdt) + offset < struct_top)
+ return 0; /* no more room :( */
+
+ memcpy(strtab + offset, s, len);
+ fdt_set_size_dt_strings(fdt, strtabsize + len);
+ return offset;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err = check_header_sw(fdt);
+ int nameoff;
+
+ if (err)
+ return err;
+
+ nameoff = find_add_string(fdt, name);
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = grab_space(fdt, sizeof(*prop) + ALIGN(len, FDT_TAGSIZE));
+ if (! prop)
+ return -FDT_ERR_NOSPACE;
+
+ prop->tag = cpu_to_fdt32(FDT_PROP);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ prop->len = cpu_to_fdt32(len);
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_finish(void *fdt)
+{
+ int err = check_header_sw(fdt);
+ char *p = (char *)fdt;
+ uint32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ if (err)
+ return err;
+
+ /* Add terminator */
+ end = grab_space(fdt, sizeof(*end));
+ if (! end)
+ return -FDT_ERR_NOSPACE;
+ *end = cpu_to_fdt32(FDT_END);
+
+ /* Relocate the string table */
+ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
+ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(fdt, newstroffset);
+
+ /* Walk the structure, correcting string offsets */
+ offset = 0;
+ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
+ if (tag == FDT_PROP) {
+ struct fdt_property *prop =
+ fdt_offset_ptr_w(fdt, offset, sizeof(*prop));
+ int nameoff;
+
+ if (! prop)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+
+ /* Finally, adjust the header */
+ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
+ fdt_set_magic(fdt, FDT_MAGIC);
+ return 0;
+}
diff --git a/kvm/libfdt/fdt_wip.c b/kvm/libfdt/fdt_wip.c
new file mode 100644
index 000000000..88e24b831
--- /dev/null
+++ b/kvm/libfdt/fdt_wip.c
@@ -0,0 +1,144 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen);
+ if (! propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ memcpy(propval, val, len);
+ return 0;
+}
+
+static void nop_region(void *start, int len)
+{
+ uint32_t *p;
+
+ for (p = start; (void *)p < (start + len); p++)
+ *p = cpu_to_fdt32(FDT_NOP);
+}
+
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ nop_region(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int _fdt_node_end_offset(void *fdt, int nodeoffset)
+{
+ int level = 0;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ return offset;
+
+ case FDT_BEGIN_NODE:
+ level++;
+ break;
+
+ case FDT_END_NODE:
+ level--;
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (level >= 0);
+
+ return nextoffset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset);
+ return 0;
+}
diff --git a/kvm/libfdt/libfdt.h b/kvm/libfdt/libfdt.h
new file mode 100644
index 000000000..8645de082
--- /dev/null
+++ b/kvm/libfdt/libfdt.h
@@ -0,0 +1,1076 @@
+#ifndef _LIBFDT_H
+#define _LIBFDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <libfdt_env.h>
+#include <fdt.h>
+
+#define FDT_FIRST_SUPPORTED_VERSION 0x10
+#define FDT_LAST_SUPPORTED_VERSION 0x11
+
+/* Error codes: informative error codes */
+#define FDT_ERR_NOTFOUND 1
+ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
+#define FDT_ERR_EXISTS 2
+ /* FDT_ERR_EXISTS: Attemped to create a node or property which
+ * already exists */
+#define FDT_ERR_NOSPACE 3
+ /* FDT_ERR_NOSPACE: Operation needed to expand the device
+ * tree, but its buffer did not have sufficient space to
+ * contain the expanded tree. Use fdt_open_into() to move the
+ * device tree to a buffer with more space. */
+
+/* Error codes: codes for bad parameters */
+#define FDT_ERR_BADOFFSET 4
+ /* FDT_ERR_BADOFFSET: Function was passed a structure block
+ * offset which is out-of-bounds, or which points to an
+ * unsuitable part of the structure for the operation. */
+#define FDT_ERR_BADPATH 5
+ /* FDT_ERR_BADPATH: Function was passed a badly formatted path
+ * (e.g. missing a leading / for a function which requires an
+ * absolute path) */
+#define FDT_ERR_BADPHANDLE 6
+ /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle
+ * value. phandle values of 0 and -1 are not permitted. */
+#define FDT_ERR_BADSTATE 7
+ /* FDT_ERR_BADSTATE: Function was passed an incomplete device
+ * tree created by the sequential-write functions, which is
+ * not sufficiently complete for the requested operation. */
+
+/* Error codes: codes for bad device tree blobs */
+#define FDT_ERR_TRUNCATED 8
+ /* FDT_ERR_TRUNCATED: Structure block of the given device tree
+ * ends without an FDT_END tag. */
+#define FDT_ERR_BADMAGIC 9
+ /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
+ * device tree at all - it is missing the flattened device
+ * tree magic number. */
+#define FDT_ERR_BADVERSION 10
+ /* FDT_ERR_BADVERSION: Given device tree has a version which
+ * can't be handled by the requested operation. For
+ * read-write functions, this may mean that fdt_open_into() is
+ * required to convert the tree to the expected version. */
+#define FDT_ERR_BADSTRUCTURE 11
+ /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
+ * structure block or other serious error (e.g. misnested
+ * nodes, or subnodes preceding properties). */
+#define FDT_ERR_BADLAYOUT 12
+ /* FDT_ERR_BADLAYOUT: For read-write functions, the given
+ * device tree has it's sub-blocks in an order that the
+ * function can't handle (memory reserve map, then structure,
+ * then strings). Use fdt_open_into() to reorganize the tree
+ * into a form suitable for the read-write operations. */
+
+/* "Can't happen" error indicating a bug in libfdt */
+#define FDT_ERR_INTERNAL 13
+ /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
+ * Should never be returned, if it is, it indicates a bug in
+ * libfdt itself. */
+
+#define FDT_ERR_MAX 13
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these) */
+/**********************************************************************/
+
+const void *fdt_offset_ptr(const void *fdt, int offset, int checklen);
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+ return (void *)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+/**********************************************************************/
+/* Traversal functions */
+/**********************************************************************/
+
+int fdt_next_node(const void *fdt, int offset, int *depth);
+
+/**********************************************************************/
+/* General functions */
+/**********************************************************************/
+
+#define fdt_get_header(fdt, field) \
+ (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
+#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
+#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
+#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
+#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
+#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
+#define fdt_version(fdt) (fdt_get_header(fdt, version))
+#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
+#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
+#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
+#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
+
+#define __fdt_set_hdr(name) \
+ static inline void fdt_set_##name(void *fdt, uint32_t val) \
+ { \
+ struct fdt_header *fdth = fdt; \
+ fdth->name = cpu_to_fdt32(val); \
+ }
+__fdt_set_hdr(magic);
+__fdt_set_hdr(totalsize);
+__fdt_set_hdr(off_dt_struct);
+__fdt_set_hdr(off_dt_strings);
+__fdt_set_hdr(off_mem_rsvmap);
+__fdt_set_hdr(version);
+__fdt_set_hdr(last_comp_version);
+__fdt_set_hdr(boot_cpuid_phys);
+__fdt_set_hdr(size_dt_strings);
+__fdt_set_hdr(size_dt_struct);
+#undef __fdt_set_hdr
+
+/**
+ * fdt_check_header - sanity check a device tree or possible device tree
+ * @fdt: pointer to data which might be a flattened device tree
+ *
+ * fdt_check_header() checks that the given buffer contains what
+ * appears to be a flattened device tree with sane information in its
+ * header.
+ *
+ * returns:
+ * 0, if the buffer appears to contain a valid device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings, as above
+ */
+int fdt_check_header(const void *fdt);
+
+/**
+ * fdt_move - move a device tree around in memory
+ * @fdt: pointer to the device tree to move
+ * @buf: pointer to memory where the device is to be moved
+ * @bufsize: size of the memory space at buf
+ *
+ * fdt_move() relocates, if possible, the device tree blob located at
+ * fdt to the buffer at buf of size bufsize. The buffer may overlap
+ * with the existing device tree blob at fdt. Therefore,
+ * fdt_move(fdt, fdt, fdt_totalsize(fdt))
+ * should always succeed.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_move(const void *fdt, void *buf, int bufsize);
+
+/**********************************************************************/
+/* Read-only functions */
+/**********************************************************************/
+
+/**
+ * fdt_string - retreive a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ *
+ * fdt_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds
+ */
+const char *fdt_string(const void *fdt, int stroffset);
+
+/**
+ * fdt_num_mem_rsv - retreive the number of memory reserve map entries
+ * @fdt: pointer to the device tree blob
+ *
+ * Returns the number of entries in the device tree blob's memory
+ * reservation map. This does not include the terminating 0,0 entry
+ * or any other (0,0) entries reserved for expansion.
+ *
+ * returns:
+ * the number of entries
+ */
+int fdt_num_mem_rsv(const void *fdt);
+
+/**
+ * fdt_get_mem_rsv - retreive one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: pointers to 64-bit variables
+ *
+ * On success, *address and *size will contain the address and size of
+ * the n-th reserve map entry from the device tree blob, in
+ * native-endian format.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
+
+/**
+ * fdt_subnode_offset_namelen - find a subnode based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_subnode_offset(), but only examine the first
+ * namelen characters of name for matching the subnode name. This is
+ * useful for finding subnodes based on a portion of a larger string,
+ * such as a full path.
+ */
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+ const char *name, int namelen);
+/**
+ * fdt_subnode_offset - find a subnode of a given node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_subnode_offset() finds a subnode of the node at structure block
+ * offset parentoffset with the given name. name may include a unit
+ * address, in which case fdt_subnode_offset() will find the subnode
+ * with that unit address, or the unit address may be omitted, in
+ * which case fdt_subnode_offset() will find an arbitrary subnode
+ * whose name excluding unit address matches the given name.
+ *
+ * returns:
+ * structure block offset of the requested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_path_offset - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ *
+ * fdt_path_offset() finds a node of a given path in the device tree.
+ * Each path component may omit the unit address portion, but the
+ * results of this are undefined if any such path component is
+ * ambiguous (that is if there are multiple nodes at the relevant
+ * level matching the given component, differentiated only by unit
+ * address).
+ *
+ * returns:
+ * structure block offset of the node with the requested path (>=0), on success
+ * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
+ * -FDT_ERR_NOTFOUND, if the requested node does not exist
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_path_offset(const void *fdt, const char *path);
+
+/**
+ * fdt_get_name - retreive the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the starting node
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_name() retrieves the name (including unit address) of the
+ * device tree node at structure block offset nodeoffset. If lenp is
+ * non-NULL, the length of this name is also returned, in the integer
+ * pointed to by lenp.
+ *
+ * returns:
+ * pointer to the node's name, on success
+ * If lenp is non-NULL, *lenp contains the length of that name (>=0)
+ * NULL, on error
+ * if lenp is non-NULL *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+/**
+ * fdt_get_property - find a given property in a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property() retrieves a pointer to the fdt_property
+ * structure within the device tree blob corresponding to the property
+ * named 'name' of the node at offset nodeoffset. If lenp is
+ * non-NULL, the length of the property value also returned, in the
+ * integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
+ const char *name,
+ int *lenp)
+{
+ return (struct fdt_property *)fdt_get_property(fdt, nodeoffset,
+ name, lenp);
+}
+
+/**
+ * fdt_getprop - retrieve the value of a given property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop() retrieves a pointer to the value of the property
+ * named 'name' of the node at offset nodeoffset (this will be a
+ * pointer to within the device blob itself, not a copy of the value).
+ * If lenp is non-NULL, the length of the property value also
+ * returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return (void *)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retreive the phandle of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the node
+ *
+ * fdt_get_phandle() retrieves the phandle of the device tree node at
+ * structure block offset nodeoffset.
+ *
+ * returns:
+ * the phandle of the node at nodeoffset, on succes (!= 0, != -1)
+ * 0, if the node has no phandle, or another error occurs
+ */
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_get_path - determine the full path of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose path to find
+ * @buf: character buffer to contain the returned path (will be overwritten)
+ * @buflen: size of the character buffer at buf
+ *
+ * fdt_get_path() computes the full path of the node at offset
+ * nodeoffset, and records that path in the buffer at buf.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * 0, on success
+ * buf contains the absolute path of the node at
+ * nodeoffset, as a NUL-terminated string.
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
+ * characters and will not fit in the given buffer.
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
+
+/**
+ * fdt_supernode_atdepth_offset - find a specific ancestor of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ * @supernodedepth: depth of the ancestor to find
+ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_supernode_atdepth_offset() finds an ancestor of the given node
+ * at a specific depth from the root (where the root itself has depth
+ * 0, its immediate subnodes depth 1 and so forth). So
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
+ * will always return 0, the offset of the root node. If the node at
+ * nodeoffset has depth D, then:
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
+ * will return nodeoffset itself.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+
+ * structure block offset of the node at node offset's ancestor
+ * of depth supernodedepth (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth);
+
+/**
+ * fdt_node_depth - find the depth of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_node_depth() finds the depth of a given node. The root node
+ * has depth 0, its immediate subnodes depth 1 and so forth.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * depth of the node at nodeoffset (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_depth(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_parent_offset - find the parent of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_parent_offset() locates the parent node of a given node (that
+ * is, it finds the offset of the node which contains the node at
+ * nodeoffset as a subnode).
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset, *twice*.
+ *
+ * returns:
+ * stucture block offset of the parent of the node at nodeoffset
+ * (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_parent_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_node_offset_by_prop_value - find nodes with a given property value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @propname: property name to check
+ * @propval: property value to search for
+ * @proplen: length of the value in propval
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the first
+ * node after startoffset, which has a property named propname whose
+ * value is of length proplen and has value equal to propval; or if
+ * startoffset is -1, the very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
+ * propval, proplen);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+ * propval, proplen);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen);
+
+/**
+ * fdt_node_offset_by_phandle - find the node with a given phandle
+ * @fdt: pointer to the device tree blob
+ * @phandle: phandle value
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the node
+ * which has the given phandle value. If there is more than one node
+ * in the tree with the given phandle (an invalid tree), results are
+ * undefined.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0), on success
+ * -FDT_ERR_NOTFOUND, no node with that phandle exists
+ * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
+
+/**
+ * fdt_node_check_compatible: check a node's compatible property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ * 0, if the node has a 'compatible' property listing the given string
+ * 1, if the node has a 'compatible' property, but it does not list
+ * the given string
+ * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible);
+
+/**
+ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @compatible: 'compatible' string to match against
+ *
+ * fdt_node_offset_by_compatible() returns the offset of the first
+ * node after startoffset, which has a 'compatible' property which
+ * lists the given compatible string; or if startoffset is -1, the
+ * very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible);
+
+/**********************************************************************/
+/* Write-in-place functions */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace - change a property's value, but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * fdt_setprop_inplace() replaces the value of a given property with
+ * the data in val, of length len. This function cannot change the
+ * size of a property, and so will only work if len is equal to the
+ * current length of the property.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if len is not equal to the property's current length
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: cell (32-bit integer) value to replace the property with
+ *
+ * fdt_setprop_inplace_cell() replaces the value of a given property
+ * with the 32-bit integer cell value in val, converting val to
+ * big-endian if necessary. This function cannot change the size of a
+ * property, and so will only work if the property already exists and
+ * has length 4.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 4
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_nop_property - replace a property with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_nop_property() will replace a given property's representation
+ * in the blob with FDT_NOP tags, effectively removing it from the
+ * tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the property, and will not alter or move any other part of the
+ * tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_nop_node - replace a node (subtree) with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_nop_node() will replace a given node's representation in the
+ * blob, including all its subnodes, if any, with FDT_NOP tags,
+ * effectively removing it from the tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the node and its properties and subnodes, and will not alter or
+ * move any other part of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Sequential write functions */
+/**********************************************************************/
+
+int fdt_create(void *buf, int bufsize);
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+int fdt_finish_reservemap(void *fdt);
+int fdt_begin_node(void *fdt, const char *name);
+int fdt_property(void *fdt, const char *name, const void *val, int len);
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &val, sizeof(val));
+}
+#define fdt_property_string(fdt, name, str) \
+ fdt_property(fdt, name, str, strlen(str)+1)
+int fdt_end_node(void *fdt);
+int fdt_finish(void *fdt);
+
+/**********************************************************************/
+/* Read-write functions */
+/**********************************************************************/
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize);
+int fdt_pack(void *fdt);
+
+/**
+ * fdt_add_mem_rsv - add one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @addres, @size: 64-bit values (native endian)
+ *
+ * Adds a reserve map entry to the given blob reserving a region at
+ * address address of length size.
+ *
+ * This function will insert data into the reserve map and will
+ * therfore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new reservation entry
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
+
+/**
+ * fdt_del_mem_rsv - remove a memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @n: entry to remove
+ *
+ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
+ * the blob.
+ *
+ * This function will delete data from the reservation table and will
+ * therfore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
+ * are less than n+1 reserve map entries)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_mem_rsv(void *fdt, int n);
+
+/**
+ * fdt_set_name - change the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ * @name: name to give the node
+ *
+ * fdt_set_name() replaces the name (including unit address, if any)
+ * of the given node with the given string. NOTE: this function can't
+ * efficiently check if the new name is unique amongst the given
+ * node's siblings; results are undefined if this function is invoked
+ * with a name equal to one of the given node's siblings.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob
+ * to contain the new name
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_set_name(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_setprop - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop() sets the value of the named property in the given
+ * node to the given value and length, creeating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_cell() sets the value of the named property in the
+ * given node to the given cell value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_setprop_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value for the property
+ *
+ * fdt_setprop_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_string(fdt, nodeoffset, name, str) \
+ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
+ * fdt_delprop - delete a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_del_property() will delete the given property.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_delprop(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_add_subnode_namelen - creates a new node based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_add_subnode(), but use only the first namelen
+ * characters of name as the name of the new node. This is useful for
+ * creating subnodes based on a portion of a larger string, such as a
+ * full path.
+ */
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen);
+
+/**
+ * fdt_add_subnode - creates a new node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_add_subnode() creates a new node as a subnode of the node at
+ * structure block offset parentoffset, with the given name (which
+ * should include the unit address, if any).
+ *
+ * This function will insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+
+ * returns:
+ * structure block offset of the created nodeequested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
+ * the given name
+ * -FDT_ERR_NOSPACE, if there is insufficient free space in the
+ * blob to contain the new node
+ * -FDT_ERR_NOSPACE
+ * -FDT_ERR_BADLAYOUT
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_del_node - delete a node (subtree)
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_del_node() will remove the given node, including all its
+ * subnodes if any, from the blob.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Debugging / informational functions */
+/**********************************************************************/
+
+const char *fdt_strerror(int errval);
+
+#endif /* _LIBFDT_H */
diff --git a/kvm/libfdt/libfdt_env.h b/kvm/libfdt/libfdt_env.h
new file mode 100644
index 000000000..59f2536d2
--- /dev/null
+++ b/kvm/libfdt/libfdt_env.h
@@ -0,0 +1,22 @@
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define fdt32_to_cpu(x) (x)
+#define cpu_to_fdt32(x) (x)
+#define fdt64_to_cpu(x) (x)
+#define cpu_to_fdt64(x) (x)
+#else
+#define fdt32_to_cpu(x) (bswap_32((x)))
+#define cpu_to_fdt32(x) (bswap_32((x)))
+#define fdt64_to_cpu(x) (bswap_64((x)))
+#define cpu_to_fdt64(x) (bswap_64((x)))
+#endif
+
+#endif /* _LIBFDT_ENV_H */
diff --git a/kvm/libfdt/libfdt_internal.h b/kvm/libfdt/libfdt_internal.h
new file mode 100644
index 000000000..52e1b8d81
--- /dev/null
+++ b/kvm/libfdt/libfdt_internal.h
@@ -0,0 +1,96 @@
+#ifndef _LIBFDT_INTERNAL_H
+#define _LIBFDT_INTERNAL_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <fdt.h>
+
+#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define PALIGN(p, a) ((void *)ALIGN((unsigned long)(p), (a)))
+
+#define memeq(p, q, n) (memcmp((p), (q), (n)) == 0)
+#define streq(p, q) (strcmp((p), (q)) == 0)
+
+#define CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset);
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
+int _fdt_node_end_offset(void *fdt, int nodeoffset);
+
+static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
+{
+ return fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
+{
+ return (void *)_fdt_offset_ptr(fdt, offset);
+}
+
+static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
+{
+ const struct fdt_reserve_entry *rsv_table =
+ fdt + fdt_off_mem_rsvmap(fdt);
+
+ return rsv_table + n;
+}
+static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
+{
+ return (void *)_fdt_mem_rsv(fdt, n);
+}
+
+#define SW_MAGIC (~FDT_MAGIC)
+
+#endif /* _LIBFDT_INTERNAL_H */
diff --git a/kvm/libkvm/Makefile b/kvm/libkvm/Makefile
new file mode 100644
index 000000000..5d96e1d78
--- /dev/null
+++ b/kvm/libkvm/Makefile
@@ -0,0 +1,37 @@
+include ../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 $(LIBKVM_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 $(LIBKVM_KERNELDIR)/include/linux/kvm.h \
+ $(DESTDIR)/$(PREFIX)/include/linux/kvm.h
+ install -D $(LIBKVM_KERNELDIR)/include/linux/kvm_para.h \
+ $(DESTDIR)/$(PREFIX)/include/linux/kvm_para.h
+ 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-powerpc.mak b/kvm/libkvm/config-powerpc.mak
new file mode 100644
index 000000000..091da370d
--- /dev/null
+++ b/kvm/libkvm/config-powerpc.mak
@@ -0,0 +1,4 @@
+
+LIBDIR := /lib
+
+libkvm-$(ARCH)-objs := libkvm-powerpc.o
diff --git a/kvm/libkvm/config-s390.mak b/kvm/libkvm/config-s390.mak
new file mode 100644
index 000000000..8177e4ad0
--- /dev/null
+++ b/kvm/libkvm/config-s390.mak
@@ -0,0 +1,3 @@
+# s390 31bit mode
+LIBDIR := /lib
+libkvm-$(ARCH)-objs := libkvm-s390.o
diff --git a/kvm/libkvm/config-s390x.mak b/kvm/libkvm/config-s390x.mak
new file mode 100644
index 000000000..f08ed3d88
--- /dev/null
+++ b/kvm/libkvm/config-s390x.mak
@@ -0,0 +1,3 @@
+# s390 64 bit mode (arch=s390x)
+LIBDIR := /lib64
+libkvm-$(ARCH)-objs := libkvm-s390.o
diff --git a/kvm/libkvm/config-x86_64.mak b/kvm/libkvm/config-x86_64.mak
new file mode 100644
index 000000000..e6389775a
--- /dev/null
+++ b/kvm/libkvm/config-x86_64.mak
@@ -0,0 +1,6 @@
+
+LIBDIR := /lib64
+CFLAGS += -m64
+CFLAGS += -D__x86_64__
+
+libkvm-$(ARCH)-objs := libkvm-x86.o
diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
new file mode 100644
index 000000000..c5beacc38
--- /dev/null
+++ b/kvm/libkvm/kvm-common.h
@@ -0,0 +1,94 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_COMMON_H
+#define KVM_COMMON_H
+
+/* FIXME: share this number with kvm */
+/* FIXME: or dynamically alloc/realloc regions */
+#ifdef __s390__
+#define KVM_MAX_NUM_MEM_REGIONS 1u
+#define MAX_VCPUS 64
+#define LIBKVM_S390_ORIGIN (0UL)
+#elif __ia64__
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 256
+#else
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 16
+#endif
+
+
+/* kvm abi verison variable */
+extern int kvm_abi;
+
+/**
+ * \brief The KVM context
+ *
+ * The verbose KVM context
+ */
+
+struct kvm_context {
+ /// Filedescriptor to /dev/kvm
+ int fd;
+ int vm_fd;
+ int vcpu_fd[MAX_VCPUS];
+ struct kvm_run *run[MAX_VCPUS];
+ /// Callbacks that KVM uses to emulate various unvirtualizable functionality
+ struct kvm_callbacks *callbacks;
+ void *opaque;
+ /// is dirty pages logging enabled for all regions or not
+ int dirty_pages_log_all;
+ /// do not create in-kernel irqchip if set
+ int no_irqchip_creation;
+ /// in-kernel irqchip status
+ int irqchip_in_kernel;
+ /// do not create in-kernel pit if set
+ int no_pit_creation;
+ /// in-kernel pit status
+ int pit_in_kernel;
+ /// in-kernel coalesced mmio
+ int coalesced_mmio;
+};
+
+void init_slots(void);
+int get_free_slot(kvm_context_t kvm);
+void register_slot(int slot, unsigned long phys_addr, unsigned long len,
+ 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);
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem);
+int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu);
+
+
+void kvm_show_code(kvm_context_t kvm, int vcpu);
+
+int handle_halt(kvm_context_t kvm, int vcpu);
+int handle_shutdown(kvm_context_t kvm, void *env);
+void post_kvm_run(kvm_context_t kvm, void *env);
+int pre_kvm_run(kvm_context_t kvm, void *env);
+int handle_io_window(kvm_context_t kvm);
+int handle_debug(kvm_context_t kvm, int vcpu, void *env);
+int try_push_interrupts(kvm_context_t kvm);
+
+#endif
diff --git a/kvm/libkvm/kvm-ia64.h b/kvm/libkvm/kvm-ia64.h
new file mode 100644
index 000000000..ad87ae764
--- /dev/null
+++ b/kvm/libkvm/kvm-ia64.h
@@ -0,0 +1,31 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for x86.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_IA64_H
+#define KVM_IA64_H
+
+#include "kvm-common.h"
+
+extern int kvm_page_size;
+
+#define PAGE_SIZE kvm_page_size
+#define PAGE_MASK (~(kvm_page_size - 1))
+
+#define ia64_mf() asm volatile ("mf" ::: "memory")
+#define smp_wmb() ia64_mf()
+
+#endif
diff --git a/kvm/libkvm/kvm-powerpc.h b/kvm/libkvm/kvm-powerpc.h
new file mode 100644
index 000000000..b09511c1c
--- /dev/null
+++ b/kvm/libkvm/kvm-powerpc.h
@@ -0,0 +1,36 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for powerpc.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by: Jerone Young <jyoung5@us.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_POWERPC_H
+#define KVM_POWERPC_H
+
+#include "kvm-common.h"
+
+extern int kvm_page_size;
+
+#define PAGE_SIZE kvm_page_size
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+static inline void eieio(void)
+{
+ asm volatile("eieio" : : : "memory");
+}
+
+#define smp_wmb() eieio()
+
+#endif
diff --git a/kvm/libkvm/kvm-s390.h b/kvm/libkvm/kvm-s390.h
new file mode 100644
index 000000000..9edd9a33b
--- /dev/null
+++ b/kvm/libkvm/kvm-s390.h
@@ -0,0 +1,31 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for s390.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright 2008 IBM Corporation.
+ * Authors:
+ * Carsten Otte <cotte@de.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_S390_H
+#define KVM_S390_H
+
+#include <asm/ptrace.h>
+#include "kvm-common.h"
+
+#define PAGE_SIZE 4096ul
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+#define smp_wmb() asm volatile("" ::: "memory")
+
+#endif
diff --git a/kvm/libkvm/kvm-x86.h b/kvm/libkvm/kvm-x86.h
new file mode 100644
index 000000000..e988cb7bb
--- /dev/null
+++ b/kvm/libkvm/kvm-x86.h
@@ -0,0 +1,55 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for x86.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_X86_H
+#define KVM_X86_H
+
+#include "kvm-common.h"
+
+#define PAGE_SIZE 4096ul
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr);
+
+#ifdef KVM_CAP_VAPIC
+
+/*!
+ * \brief Enable kernel tpr access reporting
+ *
+ * When tpr access reporting is enabled, the kernel will call the
+ * ->tpr_access() callback every time the guest vcpu accesses the tpr.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to enable tpr access reporting on
+ */
+int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Disable kernel tpr access reporting
+ *
+ * Undoes the effect of kvm_enable_tpr_access_reporting().
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to disable tpr access reporting on
+ */
+int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+#endif
+
+#define smp_wmb() asm volatile("" ::: "memory")
+
+#endif
diff --git a/kvm/libkvm/libkvm-ia64.c b/kvm/libkvm/libkvm-ia64.c
new file mode 100644
index 000000000..2f1567595
--- /dev/null
+++ b/kvm/libkvm/libkvm-ia64.c
@@ -0,0 +1,82 @@
+/*
+ * libkvm-ia64.c :Kernel-based Virtual Machine control library for ia64.
+ *
+ * This library provides an API to control the kvm hardware virtualization
+ * module.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright (C) 2007 Intel
+ * Added by : Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ *
+ */
+
+#include "libkvm.h"
+#include "kvm-ia64.h"
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int kvm_arch_run(struct kvm_run *run,kvm_context_t kvm, int vcpu)
+{
+ int r = 0;
+
+ switch (run->exit_reason) {
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr, "kvm_show_code not supported yet!\n");
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr,"kvm_show_regs not supportted today!\n");
+}
+
+int kvm_create_memory_alias(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys)
+{
+ return 0;
+}
+
+int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start)
+{
+ return 0;
+}
diff --git a/kvm/libkvm/libkvm-powerpc.c b/kvm/libkvm/libkvm-powerpc.c
new file mode 100644
index 000000000..f2cd8dc32
--- /dev/null
+++ b/kvm/libkvm/libkvm-powerpc.c
@@ -0,0 +1,100 @@
+/*
+ * This file contains the powerpc specific implementation for the
+ * architecture dependent functions defined in kvm-common.h and
+ * libkvm.h
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright IBM Corp. 2007,2008
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include "libkvm.h"
+#include "kvm-powerpc.h"
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+int handle_dcr(struct kvm_run *run, kvm_context_t kvm, int vcpu)
+{
+ int ret = 0;
+
+ if (run->dcr.is_write)
+ ret = kvm->callbacks->powerpc_dcr_write(vcpu,
+ run->dcr.dcrn,
+ run->dcr.data);
+ else
+ ret = kvm->callbacks->powerpc_dcr_read(vcpu,
+ run->dcr.dcrn,
+ &(run->dcr.data));
+
+ return ret;
+}
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__);
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_regs regs;
+ int i;
+
+ if (kvm_get_regs(kvm, vcpu, &regs))
+ return;
+
+ fprintf(stderr,"guest vcpu #%d\n", vcpu);
+ fprintf(stderr,"pc: %016"PRIx64" msr: %016"PRIx64"\n",
+ regs.pc, regs.msr);
+ fprintf(stderr,"lr: %016"PRIx64" ctr: %016"PRIx64"\n",
+ regs.lr, regs.ctr);
+ fprintf(stderr,"srr0: %016"PRIx64" srr1: %016"PRIx64"\n",
+ regs.srr0, regs.srr1);
+ for (i=0; i<32; i+=4)
+ {
+ fprintf(stderr, "gpr%02d: %016"PRIx64" %016"PRIx64" %016"PRIx64
+ " %016"PRIx64"\n", i,
+ regs.gpr[i],
+ regs.gpr[i+1],
+ regs.gpr[i+2],
+ regs.gpr[i+3]);
+ }
+
+ fflush(stdout);
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu)
+{
+ int ret = 0;
+
+ switch (run->exit_reason){
+ case KVM_EXIT_DCR:
+ ret = handle_dcr(run, kvm, vcpu);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
diff --git a/kvm/libkvm/libkvm-s390.c b/kvm/libkvm/libkvm-s390.c
new file mode 100644
index 000000000..041c0ce31
--- /dev/null
+++ b/kvm/libkvm/libkvm-s390.c
@@ -0,0 +1,110 @@
+/*
+ * This file contains the s390 specific implementation for the
+ * architecture dependent functions defined in kvm-common.h and
+ * libkvm.h
+ *
+ * Copyright (C) 2006 Qumranet
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ * Carsten Otte <cotte@de.ibm.com>
+ * Christian Borntraeger <borntraeger@de.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <sys/ioctl.h>
+#include <asm/ptrace.h>
+
+#include "libkvm.h"
+#include "kvm-common.h"
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__);
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int i;
+
+ if (kvm_get_regs(kvm, vcpu, &regs))
+ return;
+
+ if (kvm_get_sregs(kvm, vcpu, &sregs))
+ return;
+
+ fprintf(stderr, "guest vcpu #%d\n", vcpu);
+ fprintf(stderr, "PSW:\t%16.16lx %16.16lx\n",
+ kvm->run[vcpu]->s390_sieic.mask,
+ kvm->run[vcpu]->s390_sieic.addr);
+ fprintf(stderr,"GPRS:");
+ for (i=0; i<15; i+=4)
+ fprintf(stderr, "\t%16.16lx %16.16lx %16.16lx %16.16lx\n",
+ regs.gprs[i],
+ regs.gprs[i+1],
+ regs.gprs[i+2],
+ regs.gprs[i+3]);
+ fprintf(stderr,"ACRS:");
+ for (i=0; i<15; i+=4)
+ fprintf(stderr, "\t%8.8x %8.8x %8.8x %8.8x\n",
+ sregs.acrs[i],
+ sregs.acrs[i+1],
+ sregs.acrs[i+2],
+ sregs.acrs[i+3]);
+
+ fprintf(stderr,"CRS:");
+ for (i=0; i<15; i+=4)
+ fprintf(stderr, "\t%16.16lx %16.16lx %16.16lx %16.16lx\n",
+ sregs.crs[i],
+ sregs.crs[i+1],
+ sregs.crs[i+2],
+ sregs.crs[i+3]);
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ return 0;
+}
+
+int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu)
+{
+ int ret = 0;
+
+ switch (run->exit_reason){
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+int kvm_s390_initial_reset(kvm_context_t kvm, int slot)
+{
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_INITIAL_RESET, NULL);
+}
+
+int kvm_s390_interrupt(kvm_context_t kvm, int slot,
+ struct kvm_s390_interrupt *kvmint)
+{
+ if (slot>=0)
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_INTERRUPT, kvmint);
+ else
+ return ioctl(kvm->vm_fd, KVM_S390_INTERRUPT, kvmint);
+}
+
+int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw)
+{
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_SET_INITIAL_PSW, &psw);
+}
+
+int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr)
+{
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_STORE_STATUS, addr);
+}
diff --git a/kvm/libkvm/libkvm-x86.c b/kvm/libkvm/libkvm-x86.c
new file mode 100644
index 000000000..a8cca151c
--- /dev/null
+++ b/kvm/libkvm/libkvm-x86.c
@@ -0,0 +1,543 @@
+#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_set_tss_addr(kvm_context_t kvm, unsigned long addr)
+{
+#ifdef KVM_CAP_SET_TSS_ADDR
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+ if (r > 0) {
+ r = ioctl(kvm->vm_fd, KVM_SET_TSS_ADDR, addr);
+ if (r == -1) {
+ fprintf(stderr, "kvm_set_tss_addr: %m\n");
+ return -errno;
+ }
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+static int kvm_init_tss(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_SET_TSS_ADDR
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+ if (r > 0) {
+ /*
+ * this address is 3 pages before the bios, and the bios should present
+ * as unavaible memory
+ */
+ r = kvm_set_tss_addr(kvm, 0xfffbd000);
+ if (r < 0) {
+ fprintf(stderr, "kvm_init_tss: unable to set tss addr\n");
+ return r;
+ }
+
+ }
+#endif
+ return 0;
+}
+
+int kvm_create_pit(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_PIT
+ int r;
+
+ kvm->pit_in_kernel = 0;
+ if (!kvm->no_pit_creation) {
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_PIT);
+ if (r > 0) {
+ r = ioctl(kvm->vm_fd, KVM_CREATE_PIT);
+ if (r >= 0)
+ kvm->pit_in_kernel = 1;
+ else {
+ fprintf(stderr, "Create kernel PIC irqchip failed\n");
+ return r;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r = 0;
+
+ r = kvm_init_tss(kvm);
+ if (r < 0)
+ return r;
+
+ r = kvm_create_pit(kvm);
+ if (r < 0)
+ return r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+#ifdef KVM_EXIT_TPR_ACCESS
+
+static int handle_tpr_access(kvm_context_t kvm, struct kvm_run *run, int vcpu)
+{
+ return kvm->callbacks->tpr_access(kvm->opaque, vcpu,
+ run->tpr_access.rip,
+ run->tpr_access.is_write);
+}
+
+
+int kvm_enable_vapic(kvm_context_t kvm, int vcpu, uint64_t vapic)
+{
+ int r;
+ struct kvm_vapic_addr va = {
+ .vapic_addr = vapic,
+ };
+
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_VAPIC_ADDR, &va);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_enable_vapic");
+ return r;
+ }
+ return 0;
+}
+
+#endif
+
+int kvm_arch_run(struct kvm_run *run,kvm_context_t kvm, int vcpu)
+{
+ int r = 0;
+
+ switch (run->exit_reason) {
+#ifdef KVM_EXIT_SET_TPR
+ case KVM_EXIT_SET_TPR:
+ break;
+#endif
+#ifdef KVM_EXIT_TPR_ACCESS
+ case KVM_EXIT_TPR_ACCESS:
+ r = handle_tpr_access(kvm, run, vcpu);
+ break;
+#endif
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+#define MAX_ALIAS_SLOTS 4
+static struct {
+ uint64_t start;
+ uint64_t len;
+} kvm_aliases[MAX_ALIAS_SLOTS];
+
+static int get_alias_slot(uint64_t start)
+{
+ int i;
+
+ for (i=0; i<MAX_ALIAS_SLOTS; i++)
+ if (kvm_aliases[i].start == start)
+ return i;
+ return -1;
+}
+static int get_free_alias_slot(void)
+{
+ int i;
+
+ for (i=0; i<MAX_ALIAS_SLOTS; i++)
+ if (kvm_aliases[i].len == 0)
+ return i;
+ return -1;
+}
+
+static void register_alias(int slot, uint64_t start, uint64_t len)
+{
+ kvm_aliases[slot].start = start;
+ kvm_aliases[slot].len = len;
+}
+
+int kvm_create_memory_alias(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys)
+{
+ struct kvm_memory_alias alias = {
+ .flags = 0,
+ .guest_phys_addr = phys_start,
+ .memory_size = len,
+ .target_phys_addr = target_phys,
+ };
+ int fd = kvm->vm_fd;
+ int r;
+ int slot;
+
+ slot = get_alias_slot(phys_start);
+ if (slot < 0)
+ slot = get_free_alias_slot();
+ if (slot < 0)
+ return -EBUSY;
+ alias.slot = slot;
+
+ r = ioctl(fd, KVM_SET_MEMORY_ALIAS, &alias);
+ if (r == -1)
+ return -errno;
+
+ register_alias(slot, phys_start, len);
+ return 0;
+}
+
+int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start)
+{
+ return kvm_create_memory_alias(kvm, phys_start, 0, 0);
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_get_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s)
+{
+ int r;
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_LAPIC, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_get_lapic");
+ }
+ return r;
+}
+
+int kvm_set_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s)
+{
+ int r;
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_LAPIC, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_set_lapic");
+ }
+ return r;
+}
+
+#endif
+
+#ifdef KVM_CAP_PIT
+
+int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s)
+{
+ int r;
+ if (!kvm->pit_in_kernel)
+ return 0;
+ r = ioctl(kvm->vm_fd, KVM_GET_PIT, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_get_pit");
+ }
+ return r;
+}
+
+int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s)
+{
+ int r;
+ if (!kvm->pit_in_kernel)
+ return 0;
+ r = ioctl(kvm->vm_fd, KVM_SET_PIT, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_set_pit");
+ }
+ return r;
+}
+
+#endif
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+#define SHOW_CODE_LEN 50
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r, n;
+ int back_offset;
+ unsigned char code;
+ char code_str[SHOW_CODE_LEN * 3 + 1];
+ unsigned long rip;
+
+ r = ioctl(fd, KVM_GET_SREGS, &sregs);
+ if (r == -1) {
+ perror("KVM_GET_SREGS");
+ return;
+ }
+ r = ioctl(fd, KVM_GET_REGS, &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;
+ *code_str = 0;
+ for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) {
+ if (n == 0)
+ strcat(code_str, " -->");
+ r = kvm->callbacks->mmio_read(kvm->opaque, rip + n, &code, 1);
+ if (r < 0) {
+ strcat(code_str, " xx");
+ continue;
+ }
+ sprintf(code_str + strlen(code_str), " %02x", code);
+ }
+ fprintf(stderr, "code:%s\n", code_str);
+}
+
+
+/*
+ * Returns available msr list. User must free.
+ */
+struct kvm_msr_list *kvm_get_msr_list(kvm_context_t kvm)
+{
+ struct kvm_msr_list sizer, *msrs;
+ int r, e;
+
+ sizer.nmsrs = 0;
+ r = ioctl(kvm->fd, KVM_GET_MSR_INDEX_LIST, &sizer);
+ if (r == -1 && errno != E2BIG)
+ return NULL;
+ msrs = malloc(sizeof *msrs + sizer.nmsrs * sizeof *msrs->indices);
+ if (!msrs) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ msrs->nmsrs = sizer.nmsrs;
+ r = ioctl(kvm->fd, KVM_GET_MSR_INDEX_LIST, msrs);
+ if (r == -1) {
+ e = errno;
+ free(msrs);
+ errno = e;
+ return NULL;
+ }
+ return msrs;
+}
+
+int kvm_get_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs,
+ int n)
+{
+ struct kvm_msrs *kmsrs = malloc(sizeof *kmsrs + n * sizeof *msrs);
+ int r, e;
+
+ if (!kmsrs) {
+ errno = ENOMEM;
+ return -1;
+ }
+ kmsrs->nmsrs = n;
+ memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_MSRS, kmsrs);
+ e = errno;
+ memcpy(msrs, kmsrs->entries, n * sizeof *msrs);
+ free(kmsrs);
+ errno = e;
+ return r;
+}
+
+int kvm_set_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs,
+ int n)
+{
+ struct kvm_msrs *kmsrs = malloc(sizeof *kmsrs + n * sizeof *msrs);
+ int r, e;
+
+ if (!kmsrs) {
+ errno = ENOMEM;
+ return -1;
+ }
+ kmsrs->nmsrs = n;
+ memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_MSRS, kmsrs);
+ e = errno;
+ free(kmsrs);
+ errno = e;
+ return r;
+}
+
+static void print_seg(FILE *file, const char *name, struct kvm_segment *seg)
+{
+ fprintf(stderr,
+ "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d"
+ " g %d avl %d)\n",
+ name, seg->selector, seg->base, seg->limit, seg->present,
+ seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g,
+ seg->avl);
+}
+
+static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt)
+{
+ fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit);
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r;
+
+ r = ioctl(fd, KVM_GET_REGS, &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;
+}
+
+#ifdef KVM_CAP_VAPIC
+
+static int tpr_access_reporting(kvm_context_t kvm, int vcpu, int enabled)
+{
+ int r;
+ struct kvm_tpr_access_ctl tac = {
+ .enabled = enabled,
+ };
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC);
+ if (r == -1 || r == 0)
+ return -ENOSYS;
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_TPR_ACCESS_REPORTING, &tac);
+ if (r == -1) {
+ r = -errno;
+ perror("KVM_TPR_ACCESS_REPORTING");
+ return r;
+ }
+ return 0;
+}
+
+int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu)
+{
+ return tpr_access_reporting(kvm, vcpu, 1);
+}
+
+int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu)
+{
+ return tpr_access_reporting(kvm, vcpu, 0);
+}
+
+#endif
diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
new file mode 100644
index 000000000..9d3139e9f
--- /dev/null
+++ b/kvm/libkvm/libkvm.c
@@ -0,0 +1,1146 @@
+/*
+ * Kernel-based Virtual Machine control library
+ *
+ * This library provides an API to control the kvm hardware virtualization
+ * module.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef __user
+#define __user /* temporary, until installed via make headers_install */
+#endif
+
+#include <linux/kvm.h>
+
+#define EXPECTED_KVM_API_VERSION 12
+
+#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION
+#error libkvm: userspace and kernel version mismatch
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+#include "libkvm.h"
+
+#if defined(__x86_64__) || defined(__i386__)
+#include "kvm-x86.h"
+#endif
+
+#if defined(__ia64__)
+#include "kvm-ia64.h"
+#endif
+
+#if defined(__powerpc__)
+#include "kvm-powerpc.h"
+#endif
+
+#if defined(__s390__)
+#include "kvm-s390.h"
+#endif
+
+//#define DEBUG_MEMREG
+#ifdef DEBUG_MEMREG
+#define DPRINTF(fmt, args...) \
+ do { fprintf(stderr, "%s:%d " fmt , __func__, __LINE__, ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+
+int kvm_abi = EXPECTED_KVM_API_VERSION;
+int kvm_page_size;
+
+struct slot_info {
+ unsigned long phys_addr;
+ unsigned long len;
+ unsigned long userspace_addr;
+ unsigned flags;
+ int logging_count;
+};
+
+struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS];
+
+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;
+
+#if defined(KVM_CAP_SET_TSS_ADDR) && !defined(__s390__)
+ tss_ext = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+#else
+ tss_ext = 0;
+#endif
+
+ /*
+ * on older kernels where the set tss ioctl is not supprted we must save
+ * slot 0 to hold the extended memory, as the vmx will use the last 3
+ * pages of this slot.
+ */
+ if (tss_ext > 0)
+ i = 0;
+ else
+ i = 1;
+
+ for (; i < KVM_MAX_NUM_MEM_REGIONS; ++i)
+ if (!slots[i].len)
+ return i;
+ return -1;
+}
+
+void register_slot(int slot, unsigned long phys_addr, unsigned long len,
+ unsigned long userspace_addr, unsigned flags)
+{
+ slots[slot].phys_addr = phys_addr;
+ slots[slot].len = len;
+ slots[slot].userspace_addr = userspace_addr;
+ slots[slot].flags = flags;
+}
+
+void free_slot(int slot)
+{
+ slots[slot].len = 0;
+ slots[slot].logging_count = 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-1) >= phys_addr)
+ return i;
+ }
+ return -1;
+}
+
+/* Returns -1 if this slot is not totally contained on any other,
+ * and the number of the slot otherwise */
+int get_container_slot(uint64_t phys_addr, unsigned long size)
+{
+ int i;
+
+ for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS ; ++i)
+ if (slots[i].len && slots[i].phys_addr <= phys_addr &&
+ (slots[i].phys_addr + slots[i].len) >= phys_addr + size)
+ return i;
+ return -1;
+}
+
+int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_addr, unsigned long size)
+{
+ int slot = get_container_slot(phys_addr, size);
+ if (slot == -1)
+ return 0;
+ return 1;
+}
+
+/*
+ * dirty pages logging control
+ */
+static int kvm_dirty_pages_log_change(kvm_context_t kvm,
+ unsigned long phys_addr,
+ unsigned flags,
+ unsigned mask)
+{
+ int r = -1;
+ int slot = get_slot(phys_addr);
+
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__);
+ return 1;
+ }
+
+ flags = (slots[slot].flags & ~mask) | flags;
+ if (flags == slots[slot].flags)
+ return 0;
+ slots[slot].flags = flags;
+
+ {
+ struct kvm_userspace_memory_region mem = {
+ .slot = slot,
+ .memory_size = slots[slot].len,
+ .guest_phys_addr = slots[slot].phys_addr,
+ .userspace_addr = slots[slot].userspace_addr,
+ .flags = slots[slot].flags,
+ };
+
+
+ DPRINTF("slot %d start %llx len %llx flags %x\n",
+ mem.slot,
+ mem.guest_phys_addr,
+ mem.memory_size,
+ mem.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
+ if (r == -1)
+ fprintf(stderr, "%s: %m\n", __FUNCTION__);
+ }
+ return r;
+}
+
+static int kvm_dirty_pages_log_change_all(kvm_context_t kvm,
+ int (*change)(kvm_context_t kvm,
+ uint64_t start,
+ uint64_t len))
+{
+ int i, r;
+
+ for (i=r=0; i<KVM_MAX_NUM_MEM_REGIONS && r==0; i++) {
+ if (slots[i].len)
+ r = change(kvm, slots[i].phys_addr, slots[i].len);
+ }
+ return r;
+}
+
+int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm,
+ uint64_t phys_addr,
+ uint64_t len)
+{
+ int slot = get_slot(phys_addr);
+
+ DPRINTF("start %"PRIx64" len %"PRIx64"\n", phys_addr, len);
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (slots[slot].logging_count++)
+ return 0;
+
+ return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr,
+ KVM_MEM_LOG_DIRTY_PAGES,
+ KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm,
+ uint64_t phys_addr,
+ uint64_t len)
+{
+ int slot = get_slot(phys_addr);
+
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (--slots[slot].logging_count)
+ return 0;
+
+ return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr,
+ 0,
+ KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+/**
+ * Enable dirty page logging for all memory regions
+ */
+int kvm_dirty_pages_log_enable_all(kvm_context_t kvm)
+{
+ if (kvm->dirty_pages_log_all)
+ return 0;
+ kvm->dirty_pages_log_all = 1;
+ return kvm_dirty_pages_log_change_all(kvm,
+ kvm_dirty_pages_log_enable_slot);
+}
+
+/**
+ * Enable dirty page logging only for memory regions that were created with
+ * dirty logging enabled (disable for all other memory regions).
+ */
+int kvm_dirty_pages_log_reset(kvm_context_t kvm)
+{
+ if (!kvm->dirty_pages_log_all)
+ return 0;
+ kvm->dirty_pages_log_all = 0;
+ return kvm_dirty_pages_log_change_all(kvm,
+ kvm_dirty_pages_log_disable_slot);
+}
+
+
+kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
+ void *opaque)
+{
+ int fd;
+ kvm_context_t kvm;
+ int r;
+
+ fd = open("/dev/kvm", O_RDWR);
+ if (fd == -1) {
+ perror("open /dev/kvm");
+ return NULL;
+ }
+ r = ioctl(fd, KVM_GET_API_VERSION, 0);
+ if (r == -1) {
+ fprintf(stderr, "kvm kernel version too old: "
+ "KVM_GET_API_VERSION ioctl not supported\n");
+ goto out_close;
+ }
+ if (r < EXPECTED_KVM_API_VERSION) {
+ fprintf(stderr, "kvm kernel version too old: "
+ "We expect API version %d or newer, but got "
+ "version %d\n",
+ EXPECTED_KVM_API_VERSION, r);
+ goto out_close;
+ }
+ if (r > EXPECTED_KVM_API_VERSION) {
+ fprintf(stderr, "kvm userspace version too old\n");
+ goto out_close;
+ }
+ kvm_abi = r;
+ kvm_page_size = getpagesize();
+ kvm = malloc(sizeof(*kvm));
+ if (kvm == NULL)
+ goto out_close;
+ memset(kvm, 0, sizeof(*kvm));
+ kvm->fd = fd;
+ kvm->vm_fd = -1;
+ kvm->callbacks = callbacks;
+ kvm->opaque = opaque;
+ kvm->dirty_pages_log_all = 0;
+ kvm->no_irqchip_creation = 0;
+ kvm->no_pit_creation = 0;
+
+ return kvm;
+ out_close:
+ close(fd);
+ return NULL;
+}
+
+void kvm_finalize(kvm_context_t kvm)
+{
+ if (kvm->vcpu_fd[0] != -1)
+ close(kvm->vcpu_fd[0]);
+ if (kvm->vm_fd != -1)
+ close(kvm->vm_fd);
+ close(kvm->fd);
+ free(kvm);
+}
+
+void kvm_disable_irqchip_creation(kvm_context_t kvm)
+{
+ kvm->no_irqchip_creation = 1;
+}
+
+void kvm_disable_pit_creation(kvm_context_t kvm)
+{
+ kvm->no_pit_creation = 1;
+}
+
+int kvm_create_vcpu(kvm_context_t kvm, int slot)
+{
+ long mmap_size;
+ int r;
+
+ r = ioctl(kvm->vm_fd, KVM_CREATE_VCPU, slot);
+ if (r == -1) {
+ r = -errno;
+ fprintf(stderr, "kvm_create_vcpu: %m\n");
+ return r;
+ }
+ kvm->vcpu_fd[slot] = r;
+ mmap_size = ioctl(kvm->fd, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (mmap_size == -1) {
+ r = -errno;
+ fprintf(stderr, "get vcpu mmap size: %m\n");
+ return r;
+ }
+ kvm->run[slot] = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ kvm->vcpu_fd[slot], 0);
+ if (kvm->run[slot] == MAP_FAILED) {
+ r = -errno;
+ fprintf(stderr, "mmap vcpu area: %m\n");
+ return r;
+ }
+ return 0;
+}
+
+int kvm_create_vm(kvm_context_t kvm)
+{
+ int fd = kvm->fd;
+
+ kvm->vcpu_fd[0] = -1;
+
+ fd = ioctl(fd, KVM_CREATE_VM, 0);
+ if (fd == -1) {
+ fprintf(stderr, "kvm_create_vm: %m\n");
+ return -1;
+ }
+ kvm->vm_fd = fd;
+ return 0;
+}
+
+static int kvm_create_default_phys_mem(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+#ifdef KVM_CAP_USER_MEMORY
+ int r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
+ if (r > 0)
+ return 0;
+ fprintf(stderr, "Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported\n");
+#else
+#error Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported
+#endif
+ return -1;
+}
+
+int kvm_check_extension(kvm_context_t kvm, int ext)
+{
+ int ret;
+
+ ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, ext);
+ if (ret > 0)
+ return 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
+ fprintf(stderr, "Create kernel PIC irqchip failed\n");
+ }
+ }
+#endif
+}
+
+int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem)
+{
+ int r;
+
+ r = kvm_create_vm(kvm);
+ if (r < 0)
+ return r;
+ r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0)
+ return r;
+ init_slots();
+ r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0)
+ return r;
+ kvm_create_irqchip(kvm);
+
+ return 0;
+}
+
+
+void *kvm_create_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len, int log, int writable)
+{
+ int r;
+ int prot = PROT_READ;
+ void *ptr;
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = len,
+ .guest_phys_addr = phys_start,
+ .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0,
+ };
+
+ if (writable)
+ prot |= PROT_WRITE;
+
+#if !defined(__s390__)
+ ptr = mmap(NULL, len, prot, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+#else
+ ptr = mmap(LIBKVM_S390_ORIGIN, len, prot | PROT_EXEC,
+ MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+#endif
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "%s: %s", __func__, strerror(errno));
+ return 0;
+ }
+
+ memset(ptr, 0, len);
+
+ memory.userspace_addr = (unsigned long)ptr;
+ memory.slot = get_free_slot(kvm);
+ DPRINTF("slot %d start %llx len %llx flags %x\n",
+ memory.slot,
+ memory.guest_phys_addr,
+ memory.memory_size,
+ memory.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "%s: %s", __func__, strerror(errno));
+ return 0;
+ }
+ register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size,
+ memory.userspace_addr, memory.flags);
+
+ return ptr;
+}
+
+int kvm_register_phys_mem(kvm_context_t kvm,
+ unsigned long phys_start, void *userspace_addr,
+ unsigned long len, int log)
+{
+
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = len,
+ .guest_phys_addr = phys_start,
+ .userspace_addr = (unsigned long)(intptr_t)userspace_addr,
+ .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0,
+ };
+ int r;
+
+ memory.slot = get_free_slot(kvm);
+ DPRINTF("memory: gpa: %llx, size: %llx, uaddr: %llx, slot: %x, flags: %lx\n",
+ memory.guest_phys_addr, memory.memory_size,
+ memory.userspace_addr, memory.slot, memory.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "create_userspace_phys_mem: %s\n", strerror(errno));
+ return -1;
+ }
+ register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size,
+ memory.userspace_addr, memory.flags);
+ return 0;
+}
+
+
+/* destroy/free a whole slot.
+ * phys_start, len and slot are the params passed to kvm_create_phys_mem()
+ */
+void kvm_destroy_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len)
+{
+ int slot;
+ int r;
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = 0,
+ .guest_phys_addr = phys_start,
+ .userspace_addr = 0,
+ .flags = 0,
+ };
+
+ slot = get_slot(phys_start);
+
+ if ((slot >= KVM_MAX_NUM_MEM_REGIONS) || (slot == -1)) {
+ fprintf(stderr, "BUG: %s: invalid parameters (slot=%d)\n",
+ __FUNCTION__, slot);
+ return;
+ }
+ if (phys_start != slots[slot].phys_addr) {
+ fprintf(stderr,
+ "WARNING: %s: phys_start is 0x%lx expecting 0x%lx\n",
+ __FUNCTION__, phys_start, slots[slot].phys_addr);
+ phys_start = slots[slot].phys_addr;
+ }
+
+ memory.slot = slot;
+ DPRINTF("slot %d start %llx len %llx flags %x\n",
+ memory.slot,
+ memory.guest_phys_addr,
+ memory.memory_size,
+ memory.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "destroy_userspace_phys_mem: %s",
+ strerror(errno));
+ return;
+ }
+
+ free_slot(memory.slot);
+}
+
+void kvm_unregister_memory_area(kvm_context_t kvm, uint64_t phys_addr, unsigned long size)
+{
+
+ int slot = get_container_slot(phys_addr, size);
+
+ if (slot != -1) {
+ DPRINTF("Unregistering memory region %llx (%lx)\n", phys_addr, size);
+ kvm_destroy_phys_mem(kvm, phys_addr, size);
+ return;
+ }
+}
+
+static int kvm_get_map(kvm_context_t kvm, int ioctl_num, int slot, void *buf)
+{
+ int r;
+ struct kvm_dirty_log log = {
+ .slot = slot,
+ };
+
+ log.dirty_bitmap = buf;
+
+ r = ioctl(kvm->vm_fd, ioctl_num, &log);
+ if (r == -1)
+ return -errno;
+ return 0;
+}
+
+int kvm_get_dirty_pages(kvm_context_t kvm, unsigned long phys_addr, void *buf)
+{
+ int slot;
+
+ slot = get_slot(phys_addr);
+ return kvm_get_map(kvm, KVM_GET_DIRTY_LOG, slot, buf);
+}
+
+#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 && (uint64_t)slots[i].phys_addr >= phys_addr)
+ && ((uint64_t)slots[i].phys_addr + slots[i].len <= end_addr)) {
+ r = kvm_get_map(kvm, KVM_GET_DIRTY_LOG, i, buf);
+ if (r)
+ return r;
+ r = cb(slots[i].phys_addr, slots[i].len, buf, opaque);
+ if (r)
+ return r;
+ }
+ }
+ return 0;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_set_irq_level(kvm_context_t kvm, int irq, int level)
+{
+ 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, void *env)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return kvm->callbacks->debug(kvm->opaque, env, &run->debug.arch);
+#else
+ return 0;
+#endif
+}
+
+int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_REGS, regs);
+}
+
+int kvm_set_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_REGS, regs);
+}
+
+int kvm_get_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_FPU, fpu);
+}
+
+int kvm_set_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_FPU, fpu);
+}
+
+int kvm_get_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *sregs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_SREGS, sregs);
+}
+
+int kvm_set_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *sregs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SREGS, sregs);
+}
+
+#ifdef KVM_CAP_MP_STATE
+int kvm_get_mpstate(kvm_context_t kvm, int vcpu, struct kvm_mp_state *mp_state)
+{
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE);
+ if (r > 0)
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_MP_STATE, mp_state);
+ return -ENOSYS;
+}
+
+int kvm_set_mpstate(kvm_context_t kvm, int vcpu, struct kvm_mp_state *mp_state)
+{
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE);
+ if (r > 0)
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_MP_STATE, mp_state);
+ return -ENOSYS;
+}
+#endif
+
+static int handle_mmio(kvm_context_t kvm, struct kvm_run *kvm_run)
+{
+ unsigned long addr = kvm_run->mmio.phys_addr;
+ void *data = kvm_run->mmio.data;
+
+ /* hack: Red Hat 7.1 generates these weird accesses. */
+ if ((addr > 0xa0000-4 && addr <= 0xa0000) && kvm_run->mmio.len == 3)
+ return 0;
+
+ if (kvm_run->mmio.is_write)
+ return kvm->callbacks->mmio_write(kvm->opaque, addr, data,
+ kvm_run->mmio.len);
+ else
+ return kvm->callbacks->mmio_read(kvm->opaque, addr, data,
+ kvm_run->mmio.len);
+}
+
+int handle_io_window(kvm_context_t kvm)
+{
+ return kvm->callbacks->io_window(kvm->opaque);
+}
+
+int handle_halt(kvm_context_t kvm, int vcpu)
+{
+ return kvm->callbacks->halt(kvm->opaque, vcpu);
+}
+
+int handle_shutdown(kvm_context_t kvm, void *env)
+{
+ return kvm->callbacks->shutdown(kvm->opaque, env);
+}
+
+int try_push_interrupts(kvm_context_t kvm)
+{
+ return kvm->callbacks->try_push_interrupts(kvm->opaque);
+}
+
+static inline void push_nmi(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_USER_NMI
+ kvm->callbacks->push_nmi(kvm->opaque);
+#endif /* KVM_CAP_USER_NMI */
+}
+
+void post_kvm_run(kvm_context_t kvm, void *env)
+{
+ kvm->callbacks->post_kvm_run(kvm->opaque, env);
+}
+
+int pre_kvm_run(kvm_context_t kvm, void *env)
+{
+ return kvm->callbacks->pre_kvm_run(kvm->opaque, env);
+}
+
+int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->if_flag;
+}
+
+int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->ready_for_interrupt_injection;
+}
+
+int kvm_run(kvm_context_t kvm, int vcpu, void *env)
+{
+ int r;
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_run *run = kvm->run[vcpu];
+
+again:
+ push_nmi(kvm);
+#if !defined(__s390__)
+ if (!kvm->irqchip_in_kernel)
+ run->request_interrupt_window = try_push_interrupts(kvm);
+#endif
+ r = pre_kvm_run(kvm, env);
+ if (r)
+ return r;
+ r = ioctl(fd, KVM_RUN, 0);
+
+ if (r == -1 && errno != EINTR && errno != EAGAIN) {
+ r = -errno;
+ post_kvm_run(kvm, env);
+ fprintf(stderr, "kvm_run: %s\n", strerror(-r));
+ return r;
+ }
+
+ post_kvm_run(kvm, env);
+
+#if defined(KVM_CAP_COALESCED_MMIO)
+ if (kvm->coalesced_mmio) {
+ struct kvm_coalesced_mmio_ring *ring = (void *)run +
+ kvm->coalesced_mmio * PAGE_SIZE;
+ while (ring->first != ring->last) {
+ kvm->callbacks->mmio_write(kvm->opaque,
+ ring->coalesced_mmio[ring->first].phys_addr,
+ &ring->coalesced_mmio[ring->first].data[0],
+ ring->coalesced_mmio[ring->first].len);
+ smp_wmb();
+ ring->first = (ring->first + 1) %
+ KVM_COALESCED_MMIO_MAX;
+ }
+ }
+#endif
+
+#if !defined(__s390__)
+ if (r == -1) {
+ r = handle_io_window(kvm);
+ goto more;
+ }
+#endif
+ if (1) {
+ switch (run->exit_reason) {
+ case KVM_EXIT_UNKNOWN:
+ fprintf(stderr, "unhandled vm exit: 0x%x vcpu_id %d\n",
+ (unsigned)run->hw.hardware_exit_reason, vcpu);
+ kvm_show_regs(kvm, vcpu);
+ abort();
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ fprintf(stderr, "kvm_run: failed entry, reason %u\n",
+ (unsigned)run->fail_entry.hardware_entry_failure_reason & 0xffff);
+ kvm_show_regs(kvm, vcpu);
+ return -ENOEXEC;
+ break;
+ case KVM_EXIT_EXCEPTION:
+ fprintf(stderr, "exception %d (%x)\n",
+ run->ex.exception,
+ run->ex.error_code);
+ kvm_show_regs(kvm, vcpu);
+ kvm_show_code(kvm, vcpu);
+ abort();
+ break;
+ case KVM_EXIT_IO:
+ r = handle_io(kvm, run, vcpu);
+ break;
+ case KVM_EXIT_DEBUG:
+ r = handle_debug(kvm, vcpu, env);
+ break;
+ case KVM_EXIT_MMIO:
+ r = handle_mmio(kvm, run);
+ break;
+ case KVM_EXIT_HLT:
+ r = handle_halt(kvm, vcpu);
+ break;
+ case KVM_EXIT_IRQ_WINDOW_OPEN:
+ break;
+ case KVM_EXIT_SHUTDOWN:
+ r = handle_shutdown(kvm, env);
+ break;
+#if defined(__s390__)
+ case KVM_EXIT_S390_SIEIC:
+ r = kvm->callbacks->s390_handle_intercept(kvm, vcpu,
+ run);
+ break;
+ case KVM_EXIT_S390_RESET:
+ r = kvm->callbacks->s390_handle_reset(kvm, vcpu, run);
+#endif
+ default:
+ if (kvm_arch_run(run, kvm, vcpu)) {
+ fprintf(stderr, "unhandled vm exit: 0x%x\n",
+ run->exit_reason);
+ kvm_show_regs(kvm, vcpu);
+ abort();
+ }
+ break;
+ }
+ }
+more:
+ if (!r)
+ goto again;
+ return r;
+}
+
+int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq)
+{
+ struct kvm_interrupt intr;
+
+ intr.irq = irq;
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_INTERRUPT, &intr);
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_guest_debug *dbg)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_GUEST_DEBUG, dbg);
+}
+#endif
+
+int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset)
+{
+ struct kvm_signal_mask *sigmask;
+ int r;
+
+ if (!sigset) {
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SIGNAL_MASK, NULL);
+ if (r == -1)
+ r = -errno;
+ return r;
+ }
+ sigmask = malloc(sizeof(*sigmask) + sizeof(*sigset));
+ if (!sigmask)
+ return -ENOMEM;
+
+ sigmask->len = 8;
+ memcpy(sigmask->sigset, sigset, sizeof(*sigset));
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SIGNAL_MASK, sigmask);
+ if (r == -1)
+ r = -errno;
+ free(sigmask);
+ return r;
+}
+
+int kvm_irqchip_in_kernel(kvm_context_t kvm)
+{
+ return kvm->irqchip_in_kernel;
+}
+
+int kvm_pit_in_kernel(kvm_context_t kvm)
+{
+ return kvm->pit_in_kernel;
+}
+
+int kvm_has_sync_mmu(kvm_context_t kvm)
+{
+ int r = 0;
+#ifdef KVM_CAP_SYNC_MMU
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU);
+#endif
+ return r;
+}
+
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_USER_NMI
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_NMI);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_init_coalesced_mmio(kvm_context_t kvm)
+{
+ int r = 0;
+ kvm->coalesced_mmio = 0;
+#ifdef KVM_CAP_COALESCED_MMIO
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO);
+ if (r > 0) {
+ kvm->coalesced_mmio = r;
+ return 0;
+ }
+#endif
+ return r;
+}
+
+int kvm_register_coalesced_mmio(kvm_context_t kvm, uint64_t addr, uint32_t size)
+{
+#ifdef KVM_CAP_COALESCED_MMIO
+ struct kvm_coalesced_mmio_zone zone;
+ int r;
+
+ if (kvm->coalesced_mmio) {
+
+ zone.addr = addr;
+ zone.size = size;
+
+ r = ioctl(kvm->vm_fd, KVM_REGISTER_COALESCED_MMIO, &zone);
+ if (r == -1) {
+ perror("kvm_register_coalesced_mmio_zone");
+ return -errno;
+ }
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+int kvm_unregister_coalesced_mmio(kvm_context_t kvm, uint64_t addr, uint32_t size)
+{
+#ifdef KVM_CAP_COALESCED_MMIO
+ struct kvm_coalesced_mmio_zone zone;
+ int r;
+
+ if (kvm->coalesced_mmio) {
+
+ zone.addr = addr;
+ zone.size = size;
+
+ r = ioctl(kvm->vm_fd, KVM_UNREGISTER_COALESCED_MMIO, &zone);
+ if (r == -1) {
+ perror("kvm_unregister_coalesced_mmio_zone");
+ return -errno;
+ }
+ DPRINTF("Unregistered coalesced mmio region for %llx (%lx)\n", addr, size);
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+int kvm_assign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_PCI_DEVICE, assigned_dev);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+int kvm_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_IRQ, assigned_irq);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+#endif
+
+int kvm_destroy_memory_region_works(kvm_context_t kvm)
+{
+ int ret = 0;
+
+#ifdef KVM_CAP_DESTROY_MEMORY_REGION_WORKS
+ ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION,
+ KVM_CAP_DESTROY_MEMORY_REGION_WORKS);
+ if (ret <= 0)
+ ret = 0;
+#endif
+ return ret;
+}
diff --git a/kvm/libkvm/libkvm.h b/kvm/libkvm/libkvm.h
new file mode 100644
index 000000000..d068fb316
--- /dev/null
+++ b/kvm/libkvm/libkvm.h
@@ -0,0 +1,721 @@
+/** \file libkvm.h
+ * libkvm API
+ */
+
+#ifndef LIBKVM_H
+#define LIBKVM_H
+
+#if defined(__s390__)
+#include <asm/ptrace.h>
+#endif
+
+#include <stdint.h>
+
+#ifndef __user
+#define __user /* temporary, until installed via make headers_install */
+#endif
+
+#include <linux/kvm.h>
+
+#include <signal.h>
+
+struct kvm_context;
+
+typedef struct kvm_context *kvm_context_t;
+
+#if defined(__x86_64__) || defined(__i386__)
+struct kvm_msr_list *kvm_get_msr_list(kvm_context_t);
+int kvm_get_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
+int kvm_set_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
+#endif
+
+/*!
+ * \brief KVM callbacks structure
+ *
+ * This structure holds pointers to various functions that KVM will call
+ * when it encounters something that cannot be virtualized, such as
+ * accessing hardware devices via MMIO or regular IO.
+ */
+struct kvm_callbacks {
+ /// For 8bit IO reads from the guest (Usually when executing 'inb')
+ int (*inb)(void *opaque, uint16_t addr, uint8_t *data);
+ /// For 16bit IO reads from the guest (Usually when executing 'inw')
+ int (*inw)(void *opaque, uint16_t addr, uint16_t *data);
+ /// For 32bit IO reads from the guest (Usually when executing 'inl')
+ int (*inl)(void *opaque, uint16_t addr, uint32_t *data);
+ /// For 8bit IO writes from the guest (Usually when executing 'outb')
+ int (*outb)(void *opaque, uint16_t addr, uint8_t data);
+ /// For 16bit IO writes from the guest (Usually when executing 'outw')
+ int (*outw)(void *opaque, uint16_t addr, uint16_t data);
+ /// For 32bit IO writes from the guest (Usually when executing 'outl')
+ int (*outl)(void *opaque, uint16_t addr, uint32_t data);
+ /// generic memory reads to unmapped memory (For MMIO devices)
+ int (*mmio_read)(void *opaque, uint64_t addr, uint8_t *data,
+ int len);
+ /// generic memory writes to unmapped memory (For MMIO devices)
+ int (*mmio_write)(void *opaque, uint64_t addr, uint8_t *data,
+ int len);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ int (*debug)(void *opaque, void *env,
+ struct kvm_debug_exit_arch *arch_info);
+#endif
+ /*!
+ * \brief Called when the VCPU issues an 'hlt' instruction.
+ *
+ * Typically, you should yeild here to prevent 100% CPU utilization
+ * on the host CPU.
+ */
+ int (*halt)(void *opaque, int vcpu);
+ int (*shutdown)(void *opaque, void *env);
+ int (*io_window)(void *opaque);
+ int (*try_push_interrupts)(void *opaque);
+#ifdef KVM_CAP_USER_NMI
+ void (*push_nmi)(void *opaque);
+#endif
+ void (*post_kvm_run)(void *opaque, void *env);
+ int (*pre_kvm_run)(void *opaque, void *env);
+ int (*tpr_access)(void *opaque, int vcpu, uint64_t rip, int is_write);
+#if defined(__powerpc__)
+ int (*powerpc_dcr_read)(int vcpu, uint32_t dcrn, uint32_t *data);
+ int (*powerpc_dcr_write)(int vcpu, uint32_t dcrn, uint32_t data);
+#endif
+#if defined(__s390__)
+ int (*s390_handle_intercept)(kvm_context_t context, int vcpu,
+ struct kvm_run *run);
+ int (*s390_handle_reset)(kvm_context_t context, int vcpu,
+ struct kvm_run *run);
+#endif
+};
+
+/*!
+ * \brief Create new KVM context
+ *
+ * This creates a new kvm_context. A KVM context is a small area of data that
+ * holds information about the KVM instance that gets created by this call.\n
+ * This should always be your first call to KVM.
+ *
+ * \param callbacks Pointer to a valid kvm_callbacks structure
+ * \param opaque Not used
+ * \return NULL on failure
+ */
+kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
+ void *opaque);
+
+/*!
+ * \brief Cleanup the KVM context
+ *
+ * Should always be called when closing down KVM.\n
+ * Exception: If kvm_init() fails, this function should not be called, as the
+ * context would be invalid
+ *
+ * \param kvm Pointer to the kvm_context that is to be freed
+ */
+void kvm_finalize(kvm_context_t kvm);
+
+/*!
+ * \brief Disable the in-kernel IRQCHIP creation
+ *
+ * In-kernel irqchip is enabled by default. If userspace irqchip is to be used,
+ * this should be called prior to kvm_create().
+ *
+ * \param kvm Pointer to the kvm_context
+ */
+void kvm_disable_irqchip_creation(kvm_context_t kvm);
+
+/*!
+ * \brief Disable the in-kernel PIT creation
+ *
+ * In-kernel pit is enabled by default. If userspace pit is to be used,
+ * this should be called prior to kvm_create().
+ *
+ * \param kvm Pointer to the kvm_context
+ */
+void kvm_disable_pit_creation(kvm_context_t kvm);
+
+/*!
+ * \brief Create new virtual machine
+ *
+ * This creates a new virtual machine, maps physical RAM to it, and creates a
+ * virtual CPU for it.\n
+ * \n
+ * Memory gets mapped for addresses 0->0xA0000, 0xC0000->phys_mem_bytes
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param phys_mem_bytes The amount of physical ram you want the VM to have
+ * \param phys_mem This pointer will be set to point to the memory that
+ * kvm_create allocates for physical RAM
+ * \return 0 on success
+ */
+int kvm_create(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **phys_mem);
+int kvm_create_vm(kvm_context_t kvm);
+int kvm_check_extension(kvm_context_t kvm, int ext);
+void kvm_create_irqchip(kvm_context_t kvm);
+
+/*!
+ * \brief Create a new virtual cpu
+ *
+ * This creates a new virtual cpu (the first vcpu is created by kvm_create()).
+ * Should be called from a thread dedicated to the vcpu.
+ *
+ * \param kvm kvm context
+ * \param slot vcpu number (> 0)
+ * \return 0 on success, -errno on failure
+ */
+int kvm_create_vcpu(kvm_context_t kvm, int slot);
+
+/*!
+ * \brief Start the VCPU
+ *
+ * This starts the VCPU and virtualization is started.\n
+ * \n
+ * This function will not return until any of these conditions are met:
+ * - An IO/MMIO handler does not return "0"
+ * - An exception that neither the guest OS, nor KVM can handle occurs
+ *
+ * \note This function will call the callbacks registered in kvm_init()
+ * to emulate those functions
+ * \note If you at any point want to interrupt the VCPU, kvm_run() will
+ * listen to the EINTR signal. This allows you to simulate external interrupts
+ * and asyncronous IO.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be started
+ * \return 0 on success, but you really shouldn't expect this function to
+ * return except for when an error has occured, or when you have sent it
+ * an EINTR signal.
+ */
+int kvm_run(kvm_context_t kvm, int vcpu, void *env);
+
+/*!
+ * \brief Get interrupt flag from on last exit to userspace
+ *
+ * This gets the CPU interrupt flag as it was on the last exit to userspace.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return interrupt flag value (0 or 1)
+ */
+int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Get the value of the APIC_BASE msr as of last exit to userspace
+ *
+ * This gets the APIC_BASE msr as it was on the last exit to userspace.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return APIC_BASE msr contents
+ */
+uint64_t kvm_get_apic_base(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Check if a vcpu is ready for interrupt injection
+ *
+ * This checks if vcpu interrupts are not masked by mov ss or sti.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return boolean indicating interrupt injection readiness
+ */
+int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Read VCPU registers
+ *
+ * This gets the GP registers from the VCPU and outputs them
+ * into a kvm_regs structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPUs GP registers, you should call kvm_set_regs()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_regs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs);
+
+/*!
+ * \brief Write VCPU registers
+ *
+ * This sets the GP registers on the VCPU from a kvm_regs structure
+ *
+ * \note When this function returns, the regs pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_regs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_set_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs);
+/*!
+ * \brief Read VCPU fpu registers
+ *
+ * This gets the FPU registers from the VCPU and outputs them
+ * into a kvm_fpu structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPU FPU registers, you should call kvm_set_fpu()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param fpu Pointer to a kvm_fpu which will be populated with the VCPUs
+ * fpu registers values
+ * \return 0 on success
+ */
+int kvm_get_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu);
+
+/*!
+ * \brief Write VCPU fpu registers
+ *
+ * This sets the FPU registers on the VCPU from a kvm_fpu structure
+ *
+ * \note When this function returns, the fpu pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param fpu Pointer to a kvm_fpu which holds the new vcpu fpu state
+ * \return 0 on success
+ */
+int kvm_set_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu);
+
+/*!
+ * \brief Read VCPU system registers
+ *
+ * This gets the non-GP registers from the VCPU and outputs them
+ * into a kvm_sregs structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPUs non-GP registers, you should call
+ * kvm_set_sregs()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_get_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *regs);
+
+/*!
+ * \brief Write VCPU system registers
+ *
+ * This sets the non-GP registers on the VCPU from a kvm_sregs structure
+ *
+ * \note When this function returns, the regs pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_set_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *regs);
+
+#ifdef KVM_CAP_MP_STATE
+/*!
+ * * \brief Read VCPU MP state
+ *
+ */
+int kvm_get_mpstate(kvm_context_t kvm, int vcpu,
+ struct kvm_mp_state *mp_state);
+
+/*!
+ * * \brief Write VCPU MP state
+ *
+ */
+int kvm_set_mpstate(kvm_context_t kvm, int vcpu,
+ struct kvm_mp_state *mp_state);
+/*!
+ * * \brief Reset VCPU MP state
+ *
+ */
+static inline int kvm_reset_mpstate(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED};
+ return kvm_set_mpstate(kvm, vcpu, &mp_state);
+}
+#endif
+
+/*!
+ * \brief Simulate an external vectored interrupt
+ *
+ * This allows you to simulate an external vectored interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param irq Vector number
+ * \return 0 on success
+ */
+int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq);
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t, int vcpu, struct kvm_guest_debug *dbg);
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Setup a vcpu's cpuid instruction emulation
+ *
+ * Set up a table of cpuid function to cpuid outputs.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be initialized
+ * \param nent number of entries to be installed
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
+ struct kvm_cpuid_entry *entries);
+
+/*!
+ * \brief 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);
+void kvm_unregister_memory_area(kvm_context_t, uint64_t phys_start,
+ unsigned long len);
+
+int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_start, unsigned long size);
+int kvm_register_phys_mem(kvm_context_t kvm,
+ unsigned long phys_start, void *userspace_addr,
+ unsigned long len, int log);
+int kvm_get_dirty_pages(kvm_context_t, unsigned long phys_addr, void *buf);
+int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long end_addr, void *buf, void*opaque,
+ int (*cb)(unsigned long start, unsigned long len,
+ void*bitmap, void *opaque));
+int kvm_register_coalesced_mmio(kvm_context_t kvm,
+ uint64_t addr, uint32_t size);
+int kvm_unregister_coalesced_mmio(kvm_context_t kvm,
+ uint64_t addr, uint32_t size);
+
+/*!
+ * \brief Create a memory alias
+ *
+ * Aliases a portion of physical memory to another portion. If the guest
+ * accesses the alias region, it will behave exactly as if it accessed
+ * the target memory.
+ */
+int kvm_create_memory_alias(kvm_context_t,
+ uint64_t phys_start, uint64_t len,
+ uint64_t target_phys);
+
+/*!
+ * \brief Destroy a memory alias
+ *
+ * Removes an alias created with kvm_create_memory_alias().
+ */
+int kvm_destroy_memory_alias(kvm_context_t, uint64_t phys_start);
+
+/*!
+ * \brief Get a bitmap of guest ram pages which are allocated to the guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param phys_addr Memory slot phys addr
+ * \param bitmap Long aligned address of a big enough bitmap (one bit per page)
+ */
+int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *bitmap);
+int kvm_get_mem_map_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long len, void *buf, void *opaque,
+ int (*cb)(unsigned long start,unsigned long len,
+ void* bitmap, void* opaque));
+int kvm_set_irq_level(kvm_context_t kvm, int irq, int level);
+
+int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len);
+int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len);
+/*!
+ * \brief Enable dirty-pages-logging for all memory regions
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_dirty_pages_log_enable_all(kvm_context_t kvm);
+
+/*!
+ * \brief Disable dirty-page-logging for some memory regions
+ *
+ * Disable dirty-pages-logging for those memory regions that were
+ * created with dirty-page-logging disabled.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_dirty_pages_log_reset(kvm_context_t kvm);
+
+/*!
+ * \brief Query whether in kernel irqchip is used
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_irqchip_in_kernel(kvm_context_t kvm);
+
+int kvm_has_sync_mmu(kvm_context_t kvm);
+
+#ifdef KVM_CAP_IRQCHIP
+/*!
+ * \brief Dump in kernel IRQCHIP contents
+ *
+ * Dump one of the in kernel irq chip devices, including PIC (master/slave)
+ * and IOAPIC into a kvm_irqchip structure
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param chip The irq chip device to be dumped
+ */
+int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip);
+
+/*!
+ * \brief Set in kernel IRQCHIP contents
+ *
+ * Write one of the in kernel irq chip devices, including PIC (master/slave)
+ * and IOAPIC
+ *
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param chip THe irq chip device to be written
+ */
+int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip);
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Get in kernel local APIC for vcpu
+ *
+ * Save the local apic state including the timer of a virtual CPU
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be accessed
+ * \param s Local apic state of the specific virtual CPU
+ */
+int kvm_get_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s);
+
+/*!
+ * \brief Set in kernel local APIC for vcpu
+ *
+ * Restore the local apic state including the timer of a virtual CPU
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be accessed
+ * \param s Local apic state of the specific virtual CPU
+ */
+int kvm_set_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s);
+
+#endif
+
+/*!
+ * \brief Simulate an NMI
+ *
+ * This allows you to simulate a non-maskable interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu);
+
+#endif
+
+/*!
+ * \brief Query wheather in kernel pit is used
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_pit_in_kernel(kvm_context_t kvm);
+
+/*!
+ * \brief Initialize coalesced MMIO
+ *
+ * Check for coalesced MMIO capability and store in context
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_init_coalesced_mmio(kvm_context_t kvm);
+
+#ifdef KVM_CAP_PIT
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Get in kernel PIT of the virtual domain
+ *
+ * Save the PIT state.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param s PIT state of the virtual domain
+ */
+int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s);
+
+/*!
+ * \brief Set in kernel PIT of the virtual domain
+ *
+ * Restore the PIT state.
+ * Timer would be retriggerred after restored.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param s PIT state of the virtual domain
+ */
+int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s);
+#endif
+
+#endif
+
+#ifdef KVM_CAP_VAPIC
+
+/*!
+ * \brief Enable kernel tpr access reporting
+ *
+ * When tpr access reporting is enabled, the kernel will call the
+ * ->tpr_access() callback every time the guest vcpu accesses the tpr.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to enable tpr access reporting on
+ */
+int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Disable kernel tpr access reporting
+ *
+ * Undoes the effect of kvm_enable_tpr_access_reporting().
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to disable tpr access reporting on
+ */
+int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+int kvm_enable_vapic(kvm_context_t kvm, int vcpu, uint64_t vapic);
+
+#endif
+
+#if defined(__s390__)
+int kvm_s390_initial_reset(kvm_context_t kvm, int slot);
+int kvm_s390_interrupt(kvm_context_t kvm, int slot,
+ struct kvm_s390_interrupt *kvmint);
+int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw);
+int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr);
+#endif
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+/*!
+ * \brief Notifies host kernel about a PCI device to be assigned to a guest
+ *
+ * Used for PCI device assignment, this function notifies the host
+ * kernel about the assigning of the physical PCI device to a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_assign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev);
+
+/*!
+ * \brief Notifies host kernel about changes to IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function notifies the host
+ * kernel about the changes in IRQ number for an assigned physical
+ * PCI device.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq);
+
+/*!
+ * \brief Determines whether destroying memory regions is allowed
+ *
+ * KVM before 2.6.29 had a bug when destroying memory regions.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_destroy_memory_region_works(kvm_context_t kvm);
+#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..cf7f8ed74
--- /dev/null
+++ b/kvm/user/Makefile
@@ -0,0 +1,59 @@
+
+include config.mak
+
+DESTDIR :=
+
+.PHONY: arch_clean clean
+
+#make sure env CFLAGS variable is not used
+CFLAGS =
+
+libgcc := $(shell $(CC) --print-libgcc-file-name)
+
+libcflat := test/lib/libcflat.a
+cflatobjs := \
+ test/lib/panic.o \
+ test/lib/printf.o \
+ test/lib/string.o
+
+#include architecure specific make rules
+include config-$(ARCH).mak
+
+# cc-option
+# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
+
+cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+CFLAGS += -O1
+CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall
+CFLAGS += $(call cc-option, -fno-stack-protector, "")
+CFLAGS += $(call cc-option, -fno-stack-protector-all, "")
+CFLAGS += -I ../libkvm
+
+LDFLAGS += $(CFLAGS) -L ../libkvm
+
+CXXFLAGS = $(autodepend-flags)
+
+autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
+
+LDFLAGS += -pthread -lrt
+
+kvmtrace_objs= kvmtrace.o
+
+kvmctl: $(kvmctl_objs)
+ $(CC) $(LDFLAGS) $^ -o $@
+
+kvmtrace: $(kvmtrace_objs)
+ $(CC) $(LDFLAGS) $^ -o $@
+
+$(libcflat): $(cflatobjs)
+ $(AR) rcs $@ $^
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c -nostdlib -o $@ $^
+
+-include .*.d
+
+clean: arch_clean
+ $(RM) kvmctl kvmtrace *.o *.a .*.d $(libcflat) $(cflatobjs)
diff --git a/kvm/user/balloon_ctl.c b/kvm/user/balloon_ctl.c
new file mode 100755
index 000000000..e65b08d59
--- /dev/null
+++ b/kvm/user/balloon_ctl.c
@@ -0,0 +1,92 @@
+/*
+ * This binary provides access to the guest's balloon driver
+ * module.
+ *
+ * Copyright (C) 2007 Qumranet
+ *
+ * Author:
+ *
+ * Dor Laor <dor.laor@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#define __user
+#include <linux/kvm.h>
+
+#define PAGE_SIZE 4096ul
+
+
+static int balloon_op(int *fd, int bytes)
+{
+ struct kvm_balloon_op bop;
+ int r;
+
+ bop.npages = bytes/PAGE_SIZE;
+ r = ioctl(*fd, KVM_BALLOON_OP, &bop);
+ if (r == -1)
+ return -errno;
+ printf("Ballon handled %d pages successfully\n", bop.npages);
+
+ return 0;
+}
+
+static int balloon_init(int *fd)
+{
+ *fd = open("/dev/kvm_balloon", O_RDWR);
+ if (*fd == -1) {
+ perror("open /dev/kvm_balloon");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ int r;
+ int bytes;
+
+ if (argc != 3) {
+ perror("Please provide op=[i|d], bytes\n");
+ return 1;
+ }
+ bytes = atoi(argv[2]);
+
+ switch (*argv[1]) {
+ case 'i':
+ break;
+ case 'd':
+ bytes = -bytes;
+ break;
+ default:
+ perror("Wrong op param\n");
+ return 1;
+ }
+
+ if (balloon_init(&fd)) {
+ perror("balloon_init failed\n");
+ return 1;
+ }
+
+ if ((r = balloon_op(&fd, bytes))) {
+ perror("balloon_op failed\n");
+ goto out;
+ }
+
+out:
+ close(fd);
+
+ return r;
+}
+
diff --git a/kvm/user/bootstrap.lds b/kvm/user/bootstrap.lds
new file mode 100644
index 000000000..fd0a4f8a9
--- /dev/null
+++ b/kvm/user/bootstrap.lds
@@ -0,0 +1,15 @@
+OUTPUT_FORMAT(binary)
+
+SECTIONS
+{
+ . = 0;
+ stext = .;
+ .text : { *(.init) *(.text) }
+ . = ALIGN(4K);
+ .data : { *(.data) }
+ . = ALIGN(16);
+ .bss : { *(.bss) }
+ . = ALIGN(4K);
+ edata = .;
+}
+
diff --git a/kvm/user/config-i386.mak b/kvm/user/config-i386.mak
new file mode 100644
index 000000000..09175d579
--- /dev/null
+++ b/kvm/user/config-i386.mak
@@ -0,0 +1,10 @@
+TEST_DIR=test/x86
+cstart.o = $(TEST_DIR)/cstart.o
+bits = 32
+ldarch = elf32-i386
+CFLAGS += -D__i386__
+CFLAGS += -I $(KERNELDIR)/include
+
+tests=
+
+include config-x86-common.mak
diff --git a/kvm/user/config-ia64.mak b/kvm/user/config-ia64.mak
new file mode 100644
index 000000000..c4c639e2f
--- /dev/null
+++ b/kvm/user/config-ia64.mak
@@ -0,0 +1,7 @@
+bits = 64
+CFLAGS += -m64
+CFLAGS += -D__ia64__
+CFLAGS += -I $(KERNELDIR)/include
+
+all:
+
diff --git a/kvm/user/config-powerpc-440.mak b/kvm/user/config-powerpc-440.mak
new file mode 100644
index 000000000..12698e631
--- /dev/null
+++ b/kvm/user/config-powerpc-440.mak
@@ -0,0 +1,15 @@
+
+
+# for some reason binutils hates tlbsx unless we say we're 405 :(
+CFLAGS += -Wa,-m405 -I test/lib/powerpc/44x
+
+cflatobjs += \
+ test/lib/powerpc/44x/map.o \
+ test/lib/powerpc/44x/tlbwe.o \
+ test/lib/powerpc/44x/timebase.o
+
+simpletests += \
+ test/powerpc/44x/tlbsx.bin \
+ test/powerpc/44x/tlbwe_16KB.bin \
+ test/powerpc/44x/tlbwe_hole.bin \
+ test/powerpc/44x/tlbwe.bin
diff --git a/kvm/user/config-powerpc.mak b/kvm/user/config-powerpc.mak
new file mode 100644
index 000000000..dd7ef5486
--- /dev/null
+++ b/kvm/user/config-powerpc.mak
@@ -0,0 +1,39 @@
+CFLAGS += -I $(KERNELDIR)/include
+CFLAGS += -Wa,-mregnames -I test/lib
+CFLAGS += -ffreestanding
+
+cstart := test/powerpc/cstart.o
+
+cflatobjs += \
+ test/lib/powerpc/io.o
+
+$(libcflat): LDFLAGS += -nostdlib
+
+# these tests do not use libcflat
+simpletests := \
+ test/powerpc/spin.bin \
+ test/powerpc/io.bin \
+ test/powerpc/sprg.bin
+
+# theses tests use cstart.o, libcflat, and libgcc
+tests := \
+ test/powerpc/exit.bin \
+ test/powerpc/helloworld.bin
+
+include config-powerpc-$(PROCESSOR).mak
+
+
+all: kvmtrace kvmctl $(libcflat) $(simpletests) $(tests)
+
+$(simpletests): %.bin: %.o
+ $(CC) -nostdlib $^ -Wl,-T,flat.lds -o $@
+
+$(tests): %.bin: $(cstart) %.o $(libcflat)
+ $(CC) -nostdlib $^ $(libgcc) -Wl,-T,flat.lds -o $@
+
+kvmctl_objs = main-ppc.o iotable.o ../libkvm/libkvm.a
+
+arch_clean:
+ $(RM) $(simpletests) $(tests) $(cstart)
+ $(RM) $(patsubst %.bin, %.elf, $(simpletests) $(tests))
+ $(RM) $(patsubst %.bin, %.o, $(simpletests) $(tests))
diff --git a/kvm/user/config-x86-common.mak b/kvm/user/config-x86-common.mak
new file mode 100644
index 000000000..edbf6e4da
--- /dev/null
+++ b/kvm/user/config-x86-common.mak
@@ -0,0 +1,64 @@
+#This is a make file with common rules for both x86 & x86-64
+
+all: kvmctl kvmtrace test_cases
+
+kvmctl_objs= main.o iotable.o ../libkvm/libkvm.a
+balloon_ctl: balloon_ctl.o
+
+cflatobjs += \
+ test/lib/x86/io.o \
+ test/lib/x86/smp.o
+
+$(libcflat): LDFLAGS += -nostdlib
+$(libcflat): CFLAGS += -ffreestanding -I test/lib
+
+CFLAGS += -m$(bits)
+
+FLATLIBS = test/lib/libcflat.a $(libgcc)
+%.flat: %.o $(FLATLIBS)
+ $(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,flat.lds $^ $(FLATLIBS)
+
+tests-common = $(TEST_DIR)/bootstrap \
+ $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
+ $(TEST_DIR)/smp.flat $(TEST_DIR)/port80.flat \
+ $(TEST_DIR)/realmode.flat
+
+test_cases: $(tests-common) $(tests)
+
+$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I test/lib -I test/lib/x86
+
+$(TEST_DIR)/bootstrap: $(TEST_DIR)/bootstrap.o
+ $(CC) -nostdlib -o $@ -Wl,-T,bootstrap.lds $^
+
+$(TEST_DIR)/irq.flat: $(TEST_DIR)/print.o
+
+$(TEST_DIR)/access.flat: $(cstart.o) $(TEST_DIR)/access.o $(TEST_DIR)/print.o
+
+$(TEST_DIR)/hypercall.flat: $(cstart.o) $(TEST_DIR)/hypercall.o $(TEST_DIR)/print.o
+
+$(TEST_DIR)/sieve.flat: $(cstart.o) $(TEST_DIR)/sieve.o \
+ $(TEST_DIR)/print.o $(TEST_DIR)/vm.o
+
+$(TEST_DIR)/vmexit.flat: $(cstart.o) $(TEST_DIR)/vmexit.o
+
+$(TEST_DIR)/test32.flat: $(TEST_DIR)/test32.o
+
+$(TEST_DIR)/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)/tsc.flat: $(cstart.o) $(TEST_DIR)/tsc.o
+
+$(TEST_DIR)/apic.flat: $(cstart.o) $(TEST_DIR)/apic.o $(TEST_DIR)/vm.o \
+ $(TEST_DIR)/print.o
+
+$(TEST_DIR)/realmode.flat: $(TEST_DIR)/realmode.o
+ $(CC) -m32 -nostdlib -o $@ -Wl,-T,$(TEST_DIR)/realmode.lds $^
+
+$(TEST_DIR)/realmode.o: bits = 32
+
+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..b50b5402f
--- /dev/null
+++ b/kvm/user/config-x86_64.mak
@@ -0,0 +1,13 @@
+TEST_DIR=test/x86
+cstart.o = $(TEST_DIR)/cstart64.o
+bits = 64
+ldarch = elf64-x86-64
+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 $(TEST_DIR)/apic.flat
+
+include config-x86-common.mak
diff --git a/kvm/user/configure b/kvm/user/configure
new file mode 100755
index 000000000..efb8705fc
--- /dev/null
+++ b/kvm/user/configure
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+prefix=/usr/local
+kerneldir=/lib/modules/$(uname -r)/build
+cc=gcc
+ld=ld
+objcopy=objcopy
+ar=ar
+arch=`uname -m | sed -e s/i.86/i386/`
+processor="$arch"
+cross_prefix=
+
+usage() {
+ cat <<-EOF
+ Usage: $0 [options]
+
+ Options include:
+ --arch=ARCH architecture to compile for ($arch)
+ --cross-prefix=PREFIX cross compiler prefix
+ --cc=CC c compiler to use ($cc)
+ --ld=LD ld linker to use ($ld)
+ --prefix=PREFIX where to install things ($prefix)
+ --kerneldir=DIR kernel build directory for kvm.h ($kerneldir)
+EOF
+ exit 1
+}
+
+while [[ "$1" = -* ]]; do
+ opt="$1"; shift
+ arg=
+ if [[ "$opt" = *=* ]]; then
+ arg="${opt#*=}"
+ opt="${opt%%=*}"
+ fi
+ case "$opt" in
+ --prefix)
+ prefix="$arg"
+ ;;
+ --kerneldir)
+ kerneldir="$arg"
+ ;;
+ --arch)
+ arch="$arg"
+ ;;
+ --processor)
+ processor="$arg"
+ ;;
+ --cross-prefix)
+ cross_prefix="$arg"
+ ;;
+ --cc)
+ cc="$arg"
+ ;;
+ --ld)
+ ld="$arg"
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+cat <<EOF > config.mak
+PREFIX=$prefix
+KERNELDIR=$(readlink -f $kerneldir)
+ARCH=$arch
+PROCESSOR=$processor
+CC=$cross_prefix$cc
+LD=$cross_prefix$ld
+OBJCOPY=$cross_prefix$objcopy
+AR=$cross_prefix$ar
+EOF
diff --git a/kvm/user/flat.lds b/kvm/user/flat.lds
new file mode 100644
index 000000000..61f10573a
--- /dev/null
+++ b/kvm/user/flat.lds
@@ -0,0 +1,17 @@
+OUTPUT_FORMAT(binary)
+
+SECTIONS
+{
+ . = 1M;
+ stext = .;
+ .text : { *(.init) *(.text) *(.text.*) }
+ . = ALIGN(4K);
+ .data : { *(.data) }
+ . = ALIGN(16);
+ .rodata : { *(.rodata) }
+ . = ALIGN(16);
+ .bss : { *(.bss) }
+ . = ALIGN(4K);
+ edata = .;
+}
+
diff --git a/kvm/user/formats b/kvm/user/formats
new file mode 100644
index 000000000..7f4ebdbce
--- /dev/null
+++ b/kvm/user/formats
@@ -0,0 +1,31 @@
+0x00000000 %(ts)d (+%(relts)12d) unknown (0x%(event)016x) vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ 0x%(1)08x 0x%(2)08x 0x%(3)08x 0x%(4)08x 0x%(5)08x ]
+
+0x00010001 %(ts)d (+%(relts)12d) VMENTRY vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x
+0x00010002 %(ts)d (+%(relts)12d) VMEXIT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ exitcode = 0x%(1)08x, rip = 0x%(3)08x %(2)08x ]
+0x00020001 %(ts)d (+%(relts)12d) PAGE_FAULT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ]
+0x00020002 %(ts)d (+%(relts)12d) INJ_VIRQ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ]
+0x00020003 %(ts)d (+%(relts)12d) REDELIVER_EVT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ]
+0x00020004 %(ts)d (+%(relts)12d) PEND_INTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ]
+0x00020005 %(ts)d (+%(relts)12d) IO_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ]
+0x00020006 %(ts)d (+%(relts)12d) IO_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ]
+0x00020007 %(ts)d (+%(relts)12d) CR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ]
+0x00020008 %(ts)d (+%(relts)12d) CR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ]
+0x00020009 %(ts)d (+%(relts)12d) DR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ]
+0x0002000A %(ts)d (+%(relts)12d) DR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ]
+0x0002000B %(ts)d (+%(relts)12d) MSR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ]
+0x0002000C %(ts)d (+%(relts)12d) MSR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ]
+0x0002000D %(ts)d (+%(relts)12d) CPUID vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ func = 0x%(1)08x, eax = 0x%(2)08x, ebx = 0x%(3)08x, ecx = 0x%(4)08x edx = 0x%(5)08x]
+0x0002000E %(ts)d (+%(relts)12d) INTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ]
+0x0002000F %(ts)d (+%(relts)12d) NMI vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x
+0x00020010 %(ts)d (+%(relts)12d) VMMCALL vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ func = 0x%(1)08x ]
+0x00020011 %(ts)d (+%(relts)12d) HLT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x
+0x00020012 %(ts)d (+%(relts)12d) CLTS vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x
+0x00020013 %(ts)d (+%(relts)12d) LMSW vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ value = 0x%(1)08x ]
+0x00020014 %(ts)d (+%(relts)12d) APIC_ACCESS vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ offset = 0x%(1)08x ]
+0x00020015 %(ts)d (+%(relts)12d) TDP_FAULT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ]
+# ppc: tlb traces
+0x00020016 GTLB_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ]
+0x00020017 STLB_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ]
+0x00020018 STLB_INVAL vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ]
+# ppc: instruction emulation - this type is handled more complex in kvmtrace_format, but listed to show the eventid and transported data
+#0x00020019 %(ts)d (+%(relts)12d) PPC_INSTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ instr = 0x%(1)08x, pc = 0x%(2)08x, emul = 0x%(3)08x, nsec = %(4)08d ]
diff --git a/kvm/user/iotable.c b/kvm/user/iotable.c
new file mode 100644
index 000000000..91a5016c4
--- /dev/null
+++ b/kvm/user/iotable.c
@@ -0,0 +1,53 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "iotable.h"
+
+struct io_table_entry *io_table_lookup(struct io_table *io_table, uint64_t addr)
+{
+ int i;
+
+ for (i = 0; i < io_table->nr_entries; i++) {
+ if (io_table->entries[i].start <= addr &&
+ addr < io_table->entries[i].end)
+ return &io_table->entries[i];
+ }
+
+ return NULL;
+}
+
+int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
+ io_table_handler_t *handler, void *opaque)
+{
+ struct io_table_entry *entry;
+
+ if (io_table->nr_entries == MAX_IO_TABLE)
+ return -ENOSPC;
+
+ entry = &io_table->entries[io_table->nr_entries];
+ io_table->nr_entries++;
+
+ entry->start = start;
+ entry->end = start + size;
+ entry->handler = handler;
+ entry->opaque = opaque;
+
+ return 0;
+}
diff --git a/kvm/user/iotable.h b/kvm/user/iotable.h
new file mode 100644
index 000000000..cb18f2378
--- /dev/null
+++ b/kvm/user/iotable.h
@@ -0,0 +1,40 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <stdint.h>
+
+#define MAX_IO_TABLE 50
+
+typedef int (io_table_handler_t)(void *, int, int, uint64_t, uint64_t *);
+
+struct io_table_entry
+{
+ uint64_t start;
+ uint64_t end;
+ io_table_handler_t *handler;
+ void *opaque;
+};
+
+struct io_table
+{
+ int nr_entries;
+ struct io_table_entry entries[MAX_IO_TABLE];
+};
+
+struct io_table_entry *io_table_lookup(struct io_table *io_table,
+ uint64_t addr);
+int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
+ io_table_handler_t *handler, void *opaque);
diff --git a/kvm/user/kvmtrace.c b/kvm/user/kvmtrace.c
new file mode 100644
index 000000000..de3c1897f
--- /dev/null
+++ b/kvm/user/kvmtrace.c
@@ -0,0 +1,706 @@
+/*
+ * kvm tracing application
+ *
+ * This tool is used for collecting trace buffer data
+ * for kvm trace.
+ *
+ * Based on blktrace 0.99.3
+ *
+ * Copyright (C) 2005 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk>
+ * Copyright (C) 2008 Eric Liu <eric.e.liu@intel.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/statfs.h>
+#include <sys/poll.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sched.h>
+
+#ifndef __user
+#define __user
+#endif
+#include <linux/kvm.h>
+
+static char kvmtrace_version[] = "0.1";
+
+/*
+ * You may want to increase this even more, if you are logging at a high
+ * rate and see skipped/missed events
+ */
+#define BUF_SIZE (512 * 1024)
+#define BUF_NR (8)
+
+#define OFILE_BUF (128 * 1024)
+
+#define DEBUGFS_TYPE 0x64626720
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#define S_OPTS "r:o:w:?Vb:n:D:"
+static struct option l_opts[] = {
+ {
+ .name = "relay",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'r'
+ },
+ {
+ .name = "output",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'o'
+ },
+ {
+ .name = "stopwatch",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'w'
+ },
+ {
+ .name = "version",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'V'
+ },
+ {
+ .name = "buffer-size",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'b'
+ },
+ {
+ .name = "num-sub-buffers",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'n'
+ },
+ {
+ .name = "output-dir",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'D'
+ },
+ {
+ .name = NULL,
+ }
+};
+
+struct thread_information {
+ int cpu;
+ pthread_t thread;
+
+ int fd;
+ char fn[MAXPATHLEN + 64];
+
+ FILE *ofile;
+ char *ofile_buffer;
+
+ int (*get_subbuf)(struct thread_information *, unsigned int);
+ int (*read_data)(struct thread_information *, void *, unsigned int);
+
+ unsigned long long data_read;
+
+ struct kvm_trace_information *trace_info;
+
+ int exited;
+
+ /*
+ * mmap controlled output files
+ */
+ unsigned long long fs_size;
+ unsigned long long fs_max_size;
+ unsigned long fs_off;
+ void *fs_buf;
+ unsigned long fs_buf_len;
+
+};
+
+struct kvm_trace_information {
+ int fd;
+ volatile int trace_started;
+ unsigned long lost_records;
+ struct thread_information *threads;
+ unsigned long buf_size;
+ unsigned long buf_nr;
+};
+
+static struct kvm_trace_information trace_information;
+
+static int ncpus;
+static char default_debugfs_path[] = "/sys/kernel/debug";
+
+/* command line option globals */
+static char *debugfs_path;
+static char *output_name;
+static char *output_dir;
+static int stop_watch;
+static unsigned long buf_size = BUF_SIZE;
+static unsigned long buf_nr = BUF_NR;
+static unsigned int page_size;
+
+#define for_each_cpu_online(cpu) \
+ for (cpu = 0; cpu < ncpus; cpu++)
+#define for_each_tip(tip, i) \
+ for (i = 0, tip = trace_information.threads; i < ncpus; i++, tip++)
+
+#define is_done() (*(volatile int *)(&done))
+static volatile int done;
+
+#define is_trace_stopped() (*(volatile int *)(&trace_stopped))
+static volatile int trace_stopped;
+
+static void exit_trace(int status);
+
+static void handle_sigint(__attribute__((__unused__)) int sig)
+{
+ ioctl(trace_information.fd, KVM_TRACE_PAUSE);
+ done = 1;
+}
+
+static int get_lost_records()
+{
+ int fd;
+ char tmp[MAXPATHLEN + 64];
+
+ snprintf(tmp, sizeof(tmp), "%s/kvm/lost_records", debugfs_path);
+ fd = open(tmp, O_RDONLY);
+ if (fd < 0) {
+ /*
+ * this may be ok, if the kernel doesn't support dropped counts
+ */
+ if (errno == ENOENT)
+ return 0;
+
+ fprintf(stderr, "Couldn't open dropped file %s\n", tmp);
+ return -1;
+ }
+
+ if (read(fd, tmp, sizeof(tmp)) < 0) {
+ perror(tmp);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ return atoi(tmp);
+}
+
+static void wait_for_data(struct thread_information *tip, int timeout)
+{
+ struct pollfd pfd = { .fd = tip->fd, .events = POLLIN };
+
+ while (!is_done()) {
+ if (poll(&pfd, 1, timeout) < 0) {
+ perror("poll");
+ break;
+ }
+ if (pfd.revents & POLLIN)
+ break;
+ }
+}
+
+static int read_data(struct thread_information *tip, void *buf,
+ unsigned int len)
+{
+ int ret = 0;
+
+ do {
+ wait_for_data(tip, 100);
+
+ ret = read(tip->fd, buf, len);
+
+ if (!ret)
+ continue;
+ else if (ret > 0)
+ return ret;
+ else {
+ if (errno != EAGAIN) {
+ perror(tip->fn);
+ fprintf(stderr, "Thread %d failed read of %s\n",
+ tip->cpu, tip->fn);
+ break;
+ }
+ continue;
+ }
+ } while (!is_done());
+
+ return ret;
+
+}
+
+/*
+ * For file output, truncate and mmap the file appropriately
+ */
+static int mmap_subbuf(struct thread_information *tip, unsigned int maxlen)
+{
+ int ofd = fileno(tip->ofile);
+ int ret;
+ unsigned long nr;
+ unsigned long size;
+
+ /*
+ * extend file, if we have to. use chunks of 16 subbuffers.
+ */
+ if (tip->fs_off + maxlen > tip->fs_buf_len) {
+ if (tip->fs_buf) {
+ munlock(tip->fs_buf, tip->fs_buf_len);
+ munmap(tip->fs_buf, tip->fs_buf_len);
+ tip->fs_buf = NULL;
+ }
+
+ tip->fs_off = tip->fs_size & (page_size - 1);
+ nr = max(16, tip->trace_info->buf_nr);
+ size = tip->trace_info->buf_size;
+ tip->fs_buf_len = (nr * size) - tip->fs_off;
+ tip->fs_max_size += tip->fs_buf_len;
+
+ if (ftruncate(ofd, tip->fs_max_size) < 0) {
+ perror("ftruncate");
+ return -1;
+ }
+
+ tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE,
+ MAP_SHARED, ofd, tip->fs_size - tip->fs_off);
+ if (tip->fs_buf == MAP_FAILED) {
+ perror("mmap");
+ return -1;
+ }
+ mlock(tip->fs_buf, tip->fs_buf_len);
+ }
+
+ ret = tip->read_data(tip, tip->fs_buf + tip->fs_off, maxlen);
+ if (ret >= 0) {
+ tip->data_read += ret;
+ tip->fs_size += ret;
+ tip->fs_off += ret;
+ return 0;
+ }
+
+ return -1;
+}
+
+static void tip_ftrunc_final(struct thread_information *tip)
+{
+ /*
+ * truncate to right size and cleanup mmap
+ */
+ if (tip->ofile) {
+ int ofd = fileno(tip->ofile);
+
+ if (tip->fs_buf)
+ munmap(tip->fs_buf, tip->fs_buf_len);
+
+ ftruncate(ofd, tip->fs_size);
+ }
+}
+
+static void *thread_main(void *arg)
+{
+ struct thread_information *tip = arg;
+ pid_t pid = getpid();
+ cpu_set_t cpu_mask;
+
+ CPU_ZERO(&cpu_mask);
+ CPU_SET((tip->cpu), &cpu_mask);
+
+ if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
+ perror("sched_setaffinity");
+ exit_trace(1);
+ }
+
+ snprintf(tip->fn, sizeof(tip->fn), "%s/kvm/trace%d",
+ debugfs_path, tip->cpu);
+ tip->fd = open(tip->fn, O_RDONLY);
+ if (tip->fd < 0) {
+ perror(tip->fn);
+ fprintf(stderr, "Thread %d failed open of %s\n", tip->cpu,
+ tip->fn);
+ exit_trace(1);
+ }
+ while (!is_done()) {
+ if (tip->get_subbuf(tip, tip->trace_info->buf_size) < 0)
+ break;
+ }
+
+ /*
+ * trace is stopped, pull data until we get a short read
+ */
+ while (tip->get_subbuf(tip, tip->trace_info->buf_size) > 0)
+ ;
+
+ tip_ftrunc_final(tip);
+ tip->exited = 1;
+ return NULL;
+}
+
+static int fill_ofname(struct thread_information *tip, char *dst)
+{
+ struct stat sb;
+ int len = 0;
+
+ if (output_dir)
+ len = sprintf(dst, "%s/", output_dir);
+ else
+ len = sprintf(dst, "./");
+
+ if (stat(dst, &sb) < 0) {
+ if (errno != ENOENT) {
+ perror("stat");
+ return 1;
+ }
+ if (mkdir(dst, 0755) < 0) {
+ perror(dst);
+ fprintf(stderr, "Can't make output dir\n");
+ return 1;
+ }
+ }
+
+ sprintf(dst + len, "%s.kvmtrace.%d", output_name, tip->cpu);
+
+ return 0;
+}
+
+static void fill_ops(struct thread_information *tip)
+{
+ tip->get_subbuf = mmap_subbuf;
+ tip->read_data = read_data;
+}
+
+static void close_thread(struct thread_information *tip)
+{
+ if (tip->fd != -1)
+ close(tip->fd);
+ if (tip->ofile)
+ fclose(tip->ofile);
+ if (tip->ofile_buffer)
+ free(tip->ofile_buffer);
+
+ tip->fd = -1;
+ tip->ofile = NULL;
+ tip->ofile_buffer = NULL;
+}
+
+static int tip_open_output(struct thread_information *tip)
+{
+ int mode, vbuf_size;
+ char op[NAME_MAX];
+
+ if (fill_ofname(tip, op))
+ return 1;
+
+ tip->ofile = fopen(op, "w+");
+ mode = _IOFBF;
+ vbuf_size = OFILE_BUF;
+
+ if (tip->ofile == NULL) {
+ perror(op);
+ return 1;
+ }
+
+ tip->ofile_buffer = malloc(vbuf_size);
+ if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
+ perror("setvbuf");
+ close_thread(tip);
+ return 1;
+ }
+
+ fill_ops(tip);
+ return 0;
+}
+
+static int start_threads(int cpu)
+{
+ struct thread_information *tip;
+
+ tip = trace_information.threads + cpu;
+ tip->cpu = cpu;
+ tip->trace_info = &trace_information;
+ tip->fd = -1;
+
+ if (tip_open_output(tip))
+ return 1;
+
+ if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
+ perror("pthread_create");
+ close_thread(tip);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void stop_threads()
+{
+ struct thread_information *tip;
+ unsigned long ret;
+ int i;
+
+ for_each_tip(tip, i) {
+ if (tip->thread)
+ (void) pthread_join(tip->thread, (void *) &ret);
+ close_thread(tip);
+ }
+}
+
+static int start_trace(void)
+{
+ int fd;
+ struct kvm_user_trace_setup kuts;
+
+ fd = trace_information.fd = open("/dev/kvm", O_RDWR);
+ if (fd == -1) {
+ perror("/dev/kvm");
+ return 1;
+ }
+
+ memset(&kuts, 0, sizeof(kuts));
+ kuts.buf_size = trace_information.buf_size = buf_size;
+ kuts.buf_nr = trace_information.buf_nr = buf_nr;
+
+ if (ioctl(trace_information.fd , KVM_TRACE_ENABLE, &kuts) < 0) {
+ perror("KVM_TRACE_ENABLE");
+ close(fd);
+ return 1;
+ }
+ trace_information.trace_started = 1;
+
+ return 0;
+}
+
+static void cleanup_trace(void)
+{
+ if (trace_information.fd == -1)
+ return;
+
+ trace_information.lost_records = get_lost_records();
+
+ if (trace_information.trace_started) {
+ trace_information.trace_started = 0;
+ if (ioctl(trace_information.fd, KVM_TRACE_DISABLE) < 0)
+ perror("KVM_TRACE_DISABLE");
+ }
+
+ close(trace_information.fd);
+ trace_information.fd = -1;
+}
+
+static void stop_all_traces(void)
+{
+ if (!is_trace_stopped()) {
+ trace_stopped = 1;
+ stop_threads();
+ cleanup_trace();
+ }
+}
+
+static void exit_trace(int status)
+{
+ stop_all_traces();
+ exit(status);
+}
+
+static int start_kvm_trace(void)
+{
+ int i, size;
+ struct thread_information *tip;
+
+ size = ncpus * sizeof(struct thread_information);
+ tip = malloc(size);
+ if (!tip) {
+ fprintf(stderr, "Out of memory, threads (%d)\n", size);
+ return 1;
+ }
+ memset(tip, 0, size);
+ trace_information.threads = tip;
+
+ if (start_trace())
+ return 1;
+
+ for_each_cpu_online(i) {
+ if (start_threads(i)) {
+ fprintf(stderr, "Failed to start worker threads\n");
+ break;
+ }
+ }
+
+ if (i != ncpus) {
+ stop_threads();
+ cleanup_trace();
+ return 1;
+ }
+
+ return 0;
+}
+
+static void wait_for_threads(void)
+{
+ struct thread_information *tip;
+ int i, tips_running;
+
+ do {
+ tips_running = 0;
+ usleep(100000);
+
+ for_each_tip(tip, i)
+ tips_running += !tip->exited;
+
+ } while (tips_running);
+}
+
+static void show_stats(void)
+{
+ struct thread_information *tip;
+ unsigned long long data_read;
+ int i;
+
+ data_read = 0;
+ for_each_tip(tip, i) {
+ printf(" CPU%3d: %8llu KiB data\n",
+ tip->cpu, (tip->data_read + 1023) >> 10);
+ data_read += tip->data_read;
+ }
+
+ printf(" Total: lost %lu, %8llu KiB data\n",
+ trace_information.lost_records, (data_read + 1023) >> 10);
+
+ if (trace_information.lost_records)
+ fprintf(stderr, "You have lost records, "
+ "consider using a larger buffer size (-b)\n");
+}
+
+static char usage_str[] = \
+ "[ -r debugfs path ] [ -D output dir ] [ -b buffer size ]\n" \
+ "[ -n number of buffers] [ -o <output file> ] [ -w time ] [ -V ]\n\n" \
+ "\t-r Path to mounted debugfs, defaults to /sys/kernel/debug\n" \
+ "\t-o File(s) to send output to\n" \
+ "\t-D Directory to prepend to output file names\n" \
+ "\t-w Stop after defined time, in seconds\n" \
+ "\t-b Sub buffer size in KiB\n" \
+ "\t-n Number of sub buffers\n" \
+ "\t-V Print program version info\n\n";
+
+static void show_usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s %s %s", prog, kvmtrace_version, usage_str);
+ exit(EXIT_FAILURE);
+}
+
+void parse_args(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
+ switch (c) {
+ case 'r':
+ debugfs_path = optarg;
+ break;
+ case 'o':
+ output_name = optarg;
+ break;
+ case 'w':
+ stop_watch = atoi(optarg);
+ if (stop_watch <= 0) {
+ fprintf(stderr,
+ "Invalid stopwatch value (%d secs)\n",
+ stop_watch);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'V':
+ printf("%s version %s\n", argv[0], kvmtrace_version);
+ exit(EXIT_SUCCESS);
+ case 'b':
+ buf_size = strtoul(optarg, NULL, 10);
+ if (buf_size <= 0 || buf_size > 16*1024) {
+ fprintf(stderr,
+ "Invalid buffer size (%lu)\n",
+ buf_size);
+ exit(EXIT_FAILURE);
+ }
+ buf_size <<= 10;
+ break;
+ case 'n':
+ buf_nr = strtoul(optarg, NULL, 10);
+ if (buf_nr <= 0) {
+ fprintf(stderr,
+ "Invalid buffer nr (%lu)\n", buf_nr);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'D':
+ output_dir = optarg;
+ break;
+ default:
+ show_usage(argv[0]);
+ }
+ }
+
+ if (optind < argc || output_name == NULL)
+ show_usage(argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+ struct statfs st;
+
+ parse_args(argc, argv);
+
+ if (!debugfs_path)
+ debugfs_path = default_debugfs_path;
+
+ if (statfs(debugfs_path, &st) < 0) {
+ perror("statfs");
+ fprintf(stderr, "%s does not appear to be a valid path\n",
+ debugfs_path);
+ return 1;
+ } else if (st.f_type != (long) DEBUGFS_TYPE) {
+ fprintf(stderr, "%s does not appear to be a debug filesystem,"
+ " please mount debugfs.\n",
+ debugfs_path);
+ return 1;
+ }
+
+ page_size = getpagesize();
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (ncpus < 0) {
+ fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
+ return 1;
+ }
+
+ signal(SIGINT, handle_sigint);
+ signal(SIGHUP, handle_sigint);
+ signal(SIGTERM, handle_sigint);
+ signal(SIGALRM, handle_sigint);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (start_kvm_trace() != 0)
+ return 1;
+
+ if (stop_watch)
+ alarm(stop_watch);
+
+ wait_for_threads();
+ stop_all_traces();
+ show_stats();
+
+ return 0;
+}
diff --git a/kvm/user/kvmtrace_format b/kvm/user/kvmtrace_format
new file mode 100755
index 000000000..10eb1fe1f
--- /dev/null
+++ b/kvm/user/kvmtrace_format
@@ -0,0 +1,527 @@
+#!/usr/bin/env python
+
+# by Mark Williamson, (C) 2004 Intel Research Cambridge
+
+# Program for reformatting trace buffer output according to user-supplied rules
+
+import re, sys, string, signal, struct, os, getopt, operator
+
+def usage():
+ print >> sys.stderr, \
+ "Usage: " + sys.argv[0] + """ defs-file
+ Parses trace data in binary format, as output by kvmtrace and
+ reformats it according to the rules in a file of definitions. The
+ rules in this file should have the format ({ and } show grouping
+ and are not part of the syntax):
+
+ {event_id}{whitespace}{text format string}
+
+ The textual format string may include format specifiers, such as:
+ %(ts)d, %(event)d, %(pid)d %(vcpu)d %(1)d, %(2)d,
+ %(3)d, %(4)d, %(5)d
+ [ the 'd' format specifier outputs in decimal, alternatively 'x'
+ will output in hexadecimal and 'o' will output in octal ]
+
+ Which correspond to the event ID, timestamp counter, pid
+ , vcpu and the 5 data fields from the trace record. There should be
+ one such rule for each type of event.
+ Depending on your system and the volume of trace buffer data,
+ this script may not be able to keep up with the output of kvmtrace
+ if it is piped directly. In these circumstances you should have
+ kvmtrace output to a file for processing off-line.
+
+ kvmtrace_format has the following additional switches
+ -s - if this switch is set additional trace statistics are
+ created and printed at the end of the output
+ """
+ sys.exit(1)
+
+def read_defs(defs_file):
+ defs = {}
+
+ fd = open(defs_file)
+
+ reg = re.compile('(\S+)\s+(\S.*)')
+
+ while True:
+ line = fd.readline()
+ if not line:
+ break
+
+ if line[0] == '#' or line[0] == '\n':
+ continue
+
+ m = reg.match(line)
+
+ if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1)
+
+ defs[str(eval(m.group(1)))] = m.group(2)
+
+ return defs
+
+def sighand(x,y):
+ global interrupted
+ interrupted = 1
+
+# ppc instruction decoding for event type 0x00020019 (PPC_INSTR)
+# some globals for statistic summaries
+stat_ppc_instr_mnemonic = {};
+stat_ppc_instr_spr = {};
+stat_ppc_instr_dcr = {};
+stat_ppc_instr_tlb = {};
+
+def ppc_instr_print_summary(sortedlist, colname):
+ print "\n\n%14s + %10s" % (colname, "count")
+ print "%s" % (15*"-"+"+"+11*"-")
+ sum = 0
+ for value, key in sortedlist:
+ sum += key
+ print "%14s | %10d" % (value, key)
+ print "%14s = %10d" % ("sum", sum)
+
+
+def ppc_instr_summary():
+ # don't print empty statistics
+ if stat_ppc_instr_mnemonic:
+ ppc_instr_print_summary(sorted(stat_ppc_instr_mnemonic.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic")
+ if stat_ppc_instr_spr:
+ ppc_instr_print_summary(sorted(stat_ppc_instr_spr.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-spr")
+ if stat_ppc_instr_dcr:
+ ppc_instr_print_summary(sorted(stat_ppc_instr_dcr.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-dcr")
+ if stat_ppc_instr_tlb:
+ ppc_instr_print_summary(sorted(stat_ppc_instr_tlb.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-tlb")
+
+def get_op(instr):
+ return (instr >> 26);
+
+def get_xop(instr):
+ return (instr >> 1) & 0x3ff;
+
+def get_sprn(instr):
+ return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0)
+
+def get_dcrn(instr):
+ return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0);
+
+def get_tlbwe_type(instr):
+ ws = (instr >> 11) & 0x1f;
+ if ws == 0:
+ return "PAGEID"
+ elif ws == 1:
+ return "XLAT"
+ elif ws == 2:
+ return "ATTRIB"
+ else:
+ return "UNKNOWN"
+
+def get_name(instr):
+ if get_op(instr)==3:
+ return "trap"
+ elif get_op(instr)==19:
+ if get_xop(instr) == 50:
+ return "rfi"
+ else:
+ return "unknown"
+ elif get_op(instr)==31:
+ if get_xop(instr) == 83:
+ return "mfmsr"
+
+ elif get_xop(instr) == 87:
+ return "lbzx"
+
+ elif get_xop(instr) == 131:
+ return "wrtee"
+
+ elif get_xop(instr) == 146:
+ return "mtmsr"
+
+ elif get_xop(instr) == 163:
+ return "wrteei"
+
+ elif get_xop(instr) == 215:
+ return "stbx"
+
+ elif get_xop(instr) == 247:
+ return "stbux"
+
+ elif get_xop(instr) == 279:
+ return "lhzx"
+
+ elif get_xop(instr) == 311:
+ return "lhzux"
+
+ elif get_xop(instr) == 323:
+ return "mfdcr"
+
+ elif get_xop(instr) == 339:
+ return "mfspr"
+
+ elif get_xop(instr) == 407:
+ return "sthx"
+
+ elif get_xop(instr) == 439:
+ return "sthux"
+
+ elif get_xop(instr) == 451:
+ return "mtdcr"
+
+ elif get_xop(instr) == 467:
+ return "mtspr"
+
+ elif get_xop(instr) == 470:
+ return "dcbi"
+
+ elif get_xop(instr) == 534:
+ return "lwbrx"
+
+ elif get_xop(instr) == 566:
+ return "tlbsync"
+
+ elif get_xop(instr) == 662:
+ return "stwbrx"
+
+ elif get_xop(instr) == 978:
+ return "tlbwe"
+
+ elif get_xop(instr) == 914:
+ return "tlbsx"
+
+ elif get_xop(instr) == 790:
+ return "lhbrx"
+
+ elif get_xop(instr) == 918:
+ return "sthbrx"
+
+ elif get_xop(instr) == 966:
+ return "iccci"
+
+ else:
+ return "unknown"
+
+ elif get_op(instr) == 32:
+ return "lwz"
+
+ elif get_op(instr) == 33:
+ return "lwzu"
+
+ elif get_op(instr) == 34:
+ return "lbz"
+
+ elif get_op(instr) == 35:
+ return "lbzu"
+
+ elif get_op(instr) == 36:
+ return "stw"
+
+ elif get_op(instr) == 37:
+ return "stwu"
+
+ elif get_op(instr) == 38:
+ return "stb"
+
+ elif get_op(instr) == 39:
+ return "stbu"
+
+ elif get_op(instr) == 40:
+ return "lhz"
+
+ elif get_op(instr) == 41:
+ return "lhzu"
+
+ elif get_op(instr) == 44:
+ return "sth"
+
+ elif get_op(instr) == 45:
+ return "sthu"
+
+ else:
+ return "unknown"
+
+def get_sprn_name(sprn):
+ if sprn == 0x01a:
+ return "SRR0"
+ elif sprn == 0x01b:
+ return "SRR1"
+ elif sprn == 0x3b2:
+ return "MMUCR"
+ elif sprn == 0x030:
+ return "PID"
+ elif sprn == 0x03f:
+ return "IVPR"
+ elif sprn == 0x3b3:
+ return "CCR0"
+ elif sprn == 0x378:
+ return "CCR1"
+ elif sprn == 0x11f:
+ return "PVR"
+ elif sprn == 0x03d:
+ return "DEAR"
+ elif sprn == 0x03e:
+ return "ESR"
+ elif sprn == 0x134:
+ return "DBCR0"
+ elif sprn == 0x135:
+ return "DBCR1"
+ elif sprn == 0x11c:
+ return "TBWL"
+ elif sprn == 0x11d:
+ return "TBWU"
+ elif sprn == 0x016:
+ return "DEC"
+ elif sprn == 0x150:
+ return "TSR"
+ elif sprn == 0x154:
+ return "TCR"
+ elif sprn == 0x110:
+ return "SPRG0"
+ elif sprn == 0x111:
+ return "SPRG1"
+ elif sprn == 0x112:
+ return "SPRG2"
+ elif sprn == 0x113:
+ return "SPRG3"
+ elif sprn == 0x114:
+ return "SPRG4"
+ elif sprn == 0x115:
+ return "SPRG5"
+ elif sprn == 0x116:
+ return "SPRG6"
+ elif sprn == 0x117:
+ return "SPRG7"
+ elif sprn == 0x190:
+ return "IVOR0"
+ elif sprn == 0x191:
+ return "IVOR1"
+ elif sprn == 0x192:
+ return "IVOR2"
+ elif sprn == 0x193:
+ return "IVOR3"
+ elif sprn == 0x194:
+ return "IVOR4"
+ elif sprn == 0x195:
+ return "IVOR5"
+ elif sprn == 0x196:
+ return "IVOR6"
+ elif sprn == 0x197:
+ return "IVOR7"
+ elif sprn == 0x198:
+ return "IVOR8"
+ elif sprn == 0x199:
+ return "IVOR9"
+ elif sprn == 0x19a:
+ return "IVOR10"
+ elif sprn == 0x19b:
+ return "IVOR11"
+ elif sprn == 0x19c:
+ return "IVOR12"
+ elif sprn == 0x19d:
+ return "IVOR13"
+ elif sprn == 0x19e:
+ return "IVOR14"
+ elif sprn == 0x19f:
+ return "IVOR15"
+ else:
+ return "UNKNOWN"
+
+def get_special(instr):
+ name = get_name(instr);
+ if stat_ppc_instr_mnemonic.has_key(name):
+ stat_ppc_instr_mnemonic[name] += 1
+ else:
+ stat_ppc_instr_mnemonic[name] = 1
+
+ if get_op(instr) == 31:
+ if (get_xop(instr) == 339) or (get_xop(instr) == 467):
+ sprn = get_sprn(instr);
+ sprn_name = get_sprn_name(sprn);
+ stat_idx = name+"-"+sprn_name
+ if stat_ppc_instr_spr.has_key(stat_idx):
+ stat_ppc_instr_spr[stat_idx] += 1
+ else:
+ stat_ppc_instr_spr[stat_idx] = 1
+ return ("- sprn 0x%03x %8s" % (sprn, sprn_name))
+ elif (get_xop(instr) == 323 ) or (get_xop(instr) == 451):
+ dcrn = get_dcrn(instr);
+ stat_idx = name+"-"+("%04X"%dcrn)
+ if stat_ppc_instr_dcr.has_key(stat_idx):
+ stat_ppc_instr_dcr[stat_idx] += 1
+ else:
+ stat_ppc_instr_dcr[stat_idx] = 1
+ return ("- dcrn 0x%03x" % dcrn)
+ elif (get_xop(instr) == 978 ) or (get_xop(instr) == 451):
+ tlbwe_type = get_tlbwe_type(instr)
+ stat_idx = name+"-"+tlbwe_type
+ if stat_ppc_instr_tlb.has_key(stat_idx):
+ stat_ppc_instr_tlb[stat_idx] += 1
+ else:
+ stat_ppc_instr_tlb[stat_idx] = 1
+ return ("- ws -> %8s" % tlbwe_type)
+ return ""
+
+##### Main code
+
+summary = False
+
+if len(sys.argv) < 2:
+ usage()
+
+try:
+ opts, arg = getopt.getopt(sys.argv[1:], "sc:" )
+ for opt in opts:
+ if opt[0] == '-s' : summary = True
+
+except getopt.GetoptError:
+ usage()
+
+signal.signal(signal.SIGTERM, sighand)
+signal.signal(signal.SIGHUP, sighand)
+signal.signal(signal.SIGINT, sighand)
+
+interrupted = 0
+
+defs = read_defs(arg[0])
+
+# structure of trace record (as output by kvmtrace):
+# HDR(I) {TSC(Q)} D1(I) D2(I) D3(I) D4(I) D5(I)
+#
+# HDR consists of EVENT:28:, n_data:3:, ts_in:1:
+# pid:32, vcpu_id:32
+# EVENT means Event ID
+# n_data means number of data (like D1, D2, ...)
+# ts_in means Timestamp data exists(1) or not(0).
+# if ts_in == 0, TSC(Q) does not exists.
+#
+HDRREC = "<III"
+TSCREC = "<Q"
+D1REC = "<I"
+D2REC = "<II"
+D3REC = "<III"
+D4REC = "<IIII"
+D5REC = "<IIIII"
+KMAGIC = "<I"
+
+last_ts = 0
+
+i=0
+
+while not interrupted:
+ try:
+ i=i+1
+
+ if i == 1:
+ line = sys.stdin.read(struct.calcsize(KMAGIC))
+ if not line:
+ break
+ kmgc = struct.unpack(KMAGIC, line)[0]
+
+ #firstly try to parse data file as little endian
+ # if "kvmtrace-metadata".kmagic != kmagic
+ # then data file must be big endian"
+ if kmgc != 0x12345678:
+ if kmgc != 0x78563412:
+ print >> sys.stderr, "Bad data file: magic number error."
+ break;
+ else:
+ HDRREC = ">III"
+ TSCREC = ">Q"
+ D1REC = ">I"
+ D2REC = ">II"
+ D3REC = ">III"
+ D4REC = ">IIII"
+ D5REC = ">IIIII"
+ continue
+
+ line = sys.stdin.read(struct.calcsize(HDRREC))
+ if not line:
+ break
+ (event, pid, vcpu_id) = struct.unpack(HDRREC, line)
+
+ n_data = event >> 28 & 0x7
+ ts_in = event >> 31
+
+ d1 = 0
+ d2 = 0
+ d3 = 0
+ d4 = 0
+ d5 = 0
+
+ ts = 0
+
+ if ts_in == 1:
+ line = sys.stdin.read(struct.calcsize(TSCREC))
+ if not line:
+ break
+ ts = struct.unpack(TSCREC, line)[0]
+ if n_data == 1:
+ line = sys.stdin.read(struct.calcsize(D1REC))
+ if not line:
+ break
+ d1 = struct.unpack(D1REC, line)[0]
+ if n_data == 2:
+ line = sys.stdin.read(struct.calcsize(D2REC))
+ if not line:
+ break
+ (d1, d2) = struct.unpack(D2REC, line)
+ if n_data == 3:
+ line = sys.stdin.read(struct.calcsize(D3REC))
+ if not line:
+ break
+ (d1, d2, d3) = struct.unpack(D3REC, line)
+ if n_data == 4:
+ line = sys.stdin.read(struct.calcsize(D4REC))
+ if not line:
+ break
+ (d1, d2, d3, d4) = struct.unpack(D4REC, line)
+ if n_data == 5:
+ line = sys.stdin.read(struct.calcsize(D5REC))
+ if not line:
+ break
+ (d1, d2, d3, d4, d5) = struct.unpack(D5REC, line)
+
+ event &= 0x0fffffff
+
+ # provide relative TSC
+
+ if last_ts > 0 and ts_in == 1:
+ relts = ts - last_ts
+ else:
+ relts = 0
+
+ if ts_in == 1:
+ last_ts = ts
+
+ args = {'ts' : ts,
+ 'event' : event,
+ 'relts': relts,
+ 'pid' : pid,
+ 'vcpu' : vcpu_id,
+ '1' : d1,
+ '2' : d2,
+ '3' : d3,
+ '4' : d4,
+ '5' : d5 }
+
+ # some event types need more than just formats mapping they are if/elif
+ # chained here and the last default else is the mapping via formats
+ if event == 0x00020019:
+ pdata = (ts, relts, vcpu_id, pid, d1, d2, d3, get_name(d1), get_special(d1))
+ print "%d (+%12d) PPC_INSTR vcpu = 0x%08x pid = 0x%08x [ instr = 0x%08x, pc = 0x%08x, emul = %01d, mnemonic = %8s %s" % pdata
+ else:
+ try:
+ if defs.has_key(str(event)):
+ print defs[str(event)] % args
+ else:
+ if defs.has_key(str(0)): print defs[str(0)] % args
+ except TypeError:
+ if defs.has_key(str(event)):
+ print defs[str(event)]
+ print args
+ else:
+ if defs.has_key(str(0)):
+ print defs[str(0)]
+ print args
+
+ except IOError, struct.error: sys.exit()
+
+if summary:
+ ppc_instr_summary()
diff --git a/kvm/user/main-ppc.c b/kvm/user/main-ppc.c
new file mode 100644
index 000000000..5af59f846
--- /dev/null
+++ b/kvm/user/main-ppc.c
@@ -0,0 +1,383 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <libkvm.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <linux/unistd.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "iotable.h"
+
+static int gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+
+kvm_context_t kvm;
+
+#define IPI_SIGNAL (SIGRTMIN + 4)
+
+struct io_table mmio_table;
+
+static int ncpus = 1;
+static sem_t exited_sem;
+static __thread int vcpu;
+static sigset_t kernel_sigmask;
+static sigset_t ipi_sigmask;
+static uint64_t memory_size = 128 * 1024 * 1024;
+
+struct vcpu_info {
+ pid_t tid;
+};
+
+struct vcpu_info *vcpus;
+
+/* Must match flat.lds linker script */
+#define VM_TEST_LOAD_ADDRESS 0x100000
+
+static int test_debug(void *opaque, void *vcpu)
+{
+ printf("test_debug\n");
+ return 0;
+}
+
+static int test_halt(void *opaque, int vcpu)
+{
+ int n;
+
+ sigwait(&ipi_sigmask, &n);
+ return 0;
+}
+
+static int test_io_window(void *opaque)
+{
+ return 0;
+}
+
+static int test_try_push_interrupts(void *opaque)
+{
+ return 0;
+}
+
+static void test_post_kvm_run(void *opaque, void *vcpu)
+{
+}
+
+static int test_pre_kvm_run(void *opaque, void *vcpu)
+{
+ return 0;
+}
+
+static int mmio_handler(void *opaque, int len, int is_write, uint64_t offset,
+ uint64_t *data)
+{
+ int r = 0;
+
+ switch (offset) {
+ case 0: /* putc */
+ putc(*(char *)data, stdout);
+ fflush(stdout);
+ break;
+ case 1: /* exit */
+ r = *(char *)data;
+ break;
+ default:
+ printf("%s: offset %"PRIx64" len %d data %"PRIx64"\n",
+ __func__, offset, len, *(uint64_t *)data);
+ r = -EINVAL;
+ }
+
+ return r;
+}
+
+static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ struct io_table_entry *iodev;
+
+#if 0
+ printf("%s: addr %"PRIx64" len %d\n", __func__, addr, len);
+#endif
+
+ iodev = io_table_lookup(&mmio_table, addr);
+ if (!iodev) {
+ printf("couldn't find device\n");
+ return -ENODEV;
+ }
+
+ return iodev->handler(iodev->opaque, len, 0, addr - iodev->start,
+ (uint64_t *)data);
+}
+
+static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ struct io_table_entry *iodev;
+
+#if 0
+ printf("%s: addr %"PRIx64" len %d data %"PRIx64"\n",
+ __func__, addr, len, *(uint64_t *)data);
+#endif
+
+ iodev = io_table_lookup(&mmio_table, addr);
+ if (!iodev) {
+ printf("couldn't find device\n");
+ return -ENODEV;
+ }
+
+ return iodev->handler(iodev->opaque, len, 1, addr - iodev->start,
+ (uint64_t *)data);
+}
+
+static int test_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data)
+{
+ printf("%s: dcrn %04X\n", __func__, dcrn);
+ *data = 0;
+ return 0;
+}
+
+static int test_dcr_write(int vcpu, uint32_t dcrn, uint32_t data)
+{
+ printf("%s: dcrn %04X data %04X\n", __func__, dcrn, data);
+ return 0;
+}
+
+static struct kvm_callbacks test_callbacks = {
+ .mmio_read = test_mem_read,
+ .mmio_write = test_mem_write,
+ .debug = test_debug,
+ .halt = test_halt,
+ .io_window = test_io_window,
+ .try_push_interrupts = test_try_push_interrupts,
+ .post_kvm_run = test_post_kvm_run,
+ .pre_kvm_run = test_pre_kvm_run,
+ .powerpc_dcr_read = test_dcr_read,
+ .powerpc_dcr_write = test_dcr_write,
+};
+
+static unsigned long load_file(void *mem, const char *fname, int inval_icache)
+{
+ ssize_t r;
+ int fd;
+ unsigned long bytes = 0;
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ while ((r = read(fd, mem, 4096)) != -1 && r != 0) {
+ mem += r;
+ bytes += r;
+ }
+
+ if (r == -1) {
+ perror("read");
+ printf("read %d bytes\n", bytes);
+ exit(1);
+ }
+
+ return bytes;
+}
+
+#define ICACHE_LINE_SIZE 32
+
+void sync_caches(void *mem, unsigned long len)
+{
+ unsigned long i;
+
+ for (i = 0; i < len; i += ICACHE_LINE_SIZE)
+ asm volatile ("dcbst %0, %1" : : "g"(mem), "r"(i));
+ asm volatile ("sync");
+ for (i = 0; i < len; i += ICACHE_LINE_SIZE)
+ asm volatile ("icbi %0, %1" : : "g"(mem), "r"(i));
+ asm volatile ("sync; isync");
+}
+
+static void init_vcpu(int n)
+{
+ sigemptyset(&ipi_sigmask);
+ sigaddset(&ipi_sigmask, IPI_SIGNAL);
+ sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL);
+ sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask);
+ vcpus[n].tid = gettid();
+ vcpu = n;
+ kvm_set_signal_mask(kvm, n, &kernel_sigmask);
+}
+
+static void *do_create_vcpu(void *_n)
+{
+ struct kvm_regs regs;
+ int n = (long)_n;
+
+ kvm_create_vcpu(kvm, n);
+ init_vcpu(n);
+
+ kvm_get_regs(kvm, n, &regs);
+ regs.pc = VM_TEST_LOAD_ADDRESS;
+ kvm_set_regs(kvm, n, &regs);
+
+ kvm_run(kvm, n, &vcpus[n]);
+ sem_post(&exited_sem);
+ return NULL;
+}
+
+static void start_vcpu(int n)
+{
+ pthread_t thread;
+
+ pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n);
+}
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+"Usage: %s [OPTIONS] [bootstrap] flatfile\n"
+"KVM test harness.\n"
+"\n"
+" -s, --smp=NUM create a VM with NUM virtual CPUs\n"
+" -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
+" can be used to change the unit (default: `M')\n"
+" -h, --help display this help screen and exit\n"
+"\n"
+"Report bugs to <kvm-ppc@vger.kernel.org>.\n"
+ , progname);
+}
+
+static void sig_ignore(int sig)
+{
+ write(1, "boo\n", 4);
+}
+
+int main(int argc, char **argv)
+{
+ void *vm_mem;
+ unsigned long len;
+ int i;
+ const char *sopts = "s:phm:";
+ struct option lopts[] = {
+ { "smp", 1, 0, 's' },
+ { "memory", 1, 0, 'm' },
+ { "help", 0, 0, 'h' },
+ { 0 },
+ };
+ int opt_ind, ch;
+ int nb_args;
+ char *endptr;
+
+ while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
+ switch (ch) {
+ case 's':
+ ncpus = atoi(optarg);
+ break;
+ case 'm':
+ memory_size = strtoull(optarg, &endptr, 0);
+ switch (*endptr) {
+ case 'G': case 'g':
+ memory_size <<= 30;
+ break;
+ case '\0':
+ case 'M': case 'm':
+ memory_size <<= 20;
+ break;
+ case 'K': case 'k':
+ memory_size <<= 10;
+ break;
+ default:
+ fprintf(stderr,
+ "Unrecongized memory suffix: %c\n",
+ *endptr);
+ exit(1);
+ }
+ if (memory_size == 0) {
+ fprintf(stderr,
+ "Invalid memory size: 0\n");
+ exit(1);
+ }
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ case '?':
+ default:
+ fprintf(stderr,
+ "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(1);
+ }
+ }
+
+ nb_args = argc - optind;
+ if (nb_args < 1 || nb_args > 2) {
+ fprintf(stderr,
+ "Incorrect number of arguments.\n"
+ "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(1);
+ }
+
+ signal(IPI_SIGNAL, sig_ignore);
+
+ vcpus = calloc(ncpus, sizeof *vcpus);
+ if (!vcpus) {
+ fprintf(stderr, "calloc failed\n");
+ return 1;
+ }
+
+ kvm = kvm_init(&test_callbacks, 0);
+ if (!kvm) {
+ fprintf(stderr, "kvm_init failed\n");
+ return 1;
+ }
+ if (kvm_create(kvm, memory_size, &vm_mem) < 0) {
+ kvm_finalize(kvm);
+ fprintf(stderr, "kvm_create failed\n");
+ return 1;
+ }
+
+ vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1);
+
+ len = load_file(vm_mem + VM_TEST_LOAD_ADDRESS, argv[optind], 1);
+ sync_caches(vm_mem + VM_TEST_LOAD_ADDRESS, len);
+
+ io_table_register(&mmio_table, 0xf0000000, 64, mmio_handler, NULL);
+
+ sem_init(&exited_sem, 0, 0);
+ for (i = 0; i < ncpus; ++i)
+ start_vcpu(i);
+ /* Wait for all vcpus to exit. */
+ for (i = 0; i < ncpus; ++i)
+ sem_wait(&exited_sem);
+
+ return 0;
+}
diff --git a/kvm/user/main.c b/kvm/user/main.c
new file mode 100644
index 000000000..93c74187f
--- /dev/null
+++ b/kvm/user/main.c
@@ -0,0 +1,611 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <libkvm.h>
+#include "test/lib/x86/fake-apic.h"
+#include "test/x86/ioram.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <linux/unistd.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include "iotable.h"
+
+static uint8_t ioram[IORAM_LEN];
+
+static int gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+
+static int tkill(int pid, int sig)
+{
+ return syscall(__NR_tkill, pid, sig);
+}
+
+kvm_context_t kvm;
+
+#define MAX_VCPUS 4
+
+#define IPI_SIGNAL (SIGRTMIN + 4)
+
+static int ncpus = 1;
+static sem_t init_sem;
+static __thread int vcpu;
+static int apic_ipi_vector = 0xff;
+static sigset_t kernel_sigmask;
+static sigset_t ipi_sigmask;
+static uint64_t memory_size = 128 * 1024 * 1024;
+
+static struct io_table pio_table;
+
+struct vcpu_info {
+ int id;
+ pid_t tid;
+ sem_t sipi_sem;
+};
+
+struct vcpu_info *vcpus;
+
+static uint32_t apic_sipi_addr;
+
+static void apic_send_sipi(int vcpu)
+{
+ sem_post(&vcpus[vcpu].sipi_sem);
+}
+
+static void apic_send_ipi(int vcpu)
+{
+ struct vcpu_info *v;
+
+ if (vcpu < 0 || vcpu >= ncpus)
+ return;
+ v = &vcpus[vcpu];
+ tkill(v->tid, IPI_SIGNAL);
+}
+
+static int apic_io(void *opaque, int size, int is_write,
+ uint64_t addr, uint64_t *value)
+{
+ if (!is_write)
+ *value = -1u;
+
+ switch (addr - APIC_BASE) {
+ case APIC_REG_NCPU:
+ if (!is_write)
+ *value = ncpus;
+ break;
+ case APIC_REG_ID:
+ if (!is_write)
+ *value = vcpu;
+ break;
+ case APIC_REG_SIPI_ADDR:
+ if (!is_write)
+ *value = apic_sipi_addr;
+ else
+ apic_sipi_addr = *value;
+ break;
+ case APIC_REG_SEND_SIPI:
+ if (is_write)
+ apic_send_sipi(*value);
+ break;
+ case APIC_REG_IPI_VECTOR:
+ if (!is_write)
+ *value = apic_ipi_vector;
+ else
+ apic_ipi_vector = *value;
+ break;
+ case APIC_REG_SEND_IPI:
+ if (is_write)
+ apic_send_ipi(*value);
+ break;
+ }
+
+ return 0;
+}
+
+static int apic_init(void)
+{
+ return io_table_register(&pio_table, APIC_BASE,
+ APIC_SIZE, apic_io, NULL);
+}
+
+static int misc_io(void *opaque, int size, int is_write,
+ uint64_t addr, uint64_t *value)
+{
+ static int newline = 1;
+
+ if (!is_write)
+ *value = -1;
+
+ switch (addr) {
+ case 0xff: // irq injector
+ if (is_write) {
+ printf("injecting interrupt 0x%x\n", (uint8_t)*value);
+ kvm_inject_irq(kvm, 0, *value);
+ }
+ break;
+ case 0xf1: // serial
+ if (is_write) {
+ if (newline)
+ fputs("GUEST: ", stdout);
+ putchar(*value);
+ newline = *value == '\n';
+ }
+ break;
+ case 0xd1:
+ if (!is_write)
+ *value = memory_size;
+ break;
+ case 0xf4: // exit
+ if (is_write)
+ exit(*value);
+ break;
+ }
+
+ return 0;
+}
+
+static int misc_init(void)
+{
+ int err;
+
+ err = io_table_register(&pio_table, 0xff, 1, misc_io, NULL);
+ if (err < 0)
+ return err;
+
+ err = io_table_register(&pio_table, 0xf1, 1, misc_io, NULL);
+ if (err < 0)
+ return err;
+
+ err = io_table_register(&pio_table, 0xf4, 1, misc_io, NULL);
+ if (err < 0)
+ return err;
+
+ return io_table_register(&pio_table, 0xd1, 1, misc_io, NULL);
+}
+
+#define IRQCHIP_IO_BASE 0x2000
+
+static int irqchip_io(void *opaque, int size, int is_write,
+ uint64_t addr, uint64_t *value)
+{
+ addr -= IRQCHIP_IO_BASE;
+
+ if (is_write) {
+ kvm_set_irq_level(kvm, addr, *value);
+ }
+ return 0;
+}
+
+static int test_inb(void *opaque, uint16_t addr, uint8_t *value)
+{
+ struct io_table_entry *entry;
+
+ entry = io_table_lookup(&pio_table, addr);
+ if (entry) {
+ uint64_t val;
+ entry->handler(entry->opaque, 1, 0, addr, &val);
+ *value = val;
+ } else {
+ *value = -1;
+ printf("inb 0x%x\n", addr);
+ }
+
+ return 0;
+}
+
+static int test_inw(void *opaque, uint16_t addr, uint16_t *value)
+{
+ struct io_table_entry *entry;
+
+ entry = io_table_lookup(&pio_table, addr);
+ if (entry) {
+ uint64_t val;
+ entry->handler(entry->opaque, 2, 0, addr, &val);
+ *value = val;
+ } else {
+ *value = -1;
+ printf("inw 0x%x\n", addr);
+ }
+
+ return 0;
+}
+
+static int test_inl(void *opaque, uint16_t addr, uint32_t *value)
+{
+ struct io_table_entry *entry;
+
+ entry = io_table_lookup(&pio_table, addr);
+ if (entry) {
+ uint64_t val;
+ entry->handler(entry->opaque, 4, 0, addr, &val);
+ *value = val;
+ } else {
+ *value = -1;
+ printf("inl 0x%x\n", addr);
+ }
+
+ return 0;
+}
+
+static int test_outb(void *opaque, uint16_t addr, uint8_t value)
+{
+ struct io_table_entry *entry;
+
+ entry = io_table_lookup(&pio_table, addr);
+ if (entry) {
+ uint64_t val = value;
+ entry->handler(entry->opaque, 1, 1, addr, &val);
+ } else
+ printf("outb $0x%x, 0x%x\n", value, addr);
+
+ return 0;
+}
+
+static int test_outw(void *opaque, uint16_t addr, uint16_t value)
+{
+ struct io_table_entry *entry;
+
+ entry = io_table_lookup(&pio_table, addr);
+ if (entry) {
+ uint64_t val = value;
+ entry->handler(entry->opaque, 2, 1, addr, &val);
+ } else
+ printf("outw $0x%x, 0x%x\n", value, addr);
+
+ return 0;
+}
+
+static int test_outl(void *opaque, uint16_t addr, uint32_t value)
+{
+ struct io_table_entry *entry;
+
+ entry = io_table_lookup(&pio_table, addr);
+ if (entry) {
+ uint64_t val = value;
+ entry->handler(entry->opaque, 4, 1, addr, &val);
+ } else
+ printf("outl $0x%x, 0x%x\n", value, addr);
+
+ return 0;
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static int test_debug(void *opaque, void *vcpu,
+ struct kvm_debug_exit_arch *arch_info)
+{
+ printf("test_debug\n");
+ return 0;
+}
+#endif
+
+static int test_halt(void *opaque, int vcpu)
+{
+ int n;
+
+ sigwait(&ipi_sigmask, &n);
+ kvm_inject_irq(kvm, vcpus[vcpu].id, apic_ipi_vector);
+ return 0;
+}
+
+static int test_io_window(void *opaque)
+{
+ return 0;
+}
+
+static int test_try_push_interrupts(void *opaque)
+{
+ return 0;
+}
+
+#ifdef KVM_CAP_USER_NMI
+static void test_push_nmi(void *opaque)
+{
+}
+#endif
+
+static void test_post_kvm_run(void *opaque, void *vcpu)
+{
+}
+
+static int test_pre_kvm_run(void *opaque, void *vcpu)
+{
+ return 0;
+}
+
+static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ if (addr < IORAM_BASE_PHYS || addr + len > IORAM_BASE_PHYS + IORAM_LEN)
+ return 1;
+ memcpy(data, ioram + addr - IORAM_BASE_PHYS, len);
+ return 0;
+}
+
+static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ if (addr < IORAM_BASE_PHYS || addr + len > IORAM_BASE_PHYS + IORAM_LEN)
+ return 1;
+ memcpy(ioram + addr - IORAM_BASE_PHYS, data, len);
+ return 0;
+}
+
+static int test_shutdown(void *opaque, void *env)
+{
+ printf("shutdown\n");
+ kvm_show_regs(kvm, 0);
+ exit(1);
+ return 1;
+}
+
+static struct kvm_callbacks test_callbacks = {
+ .inb = test_inb,
+ .inw = test_inw,
+ .inl = test_inl,
+ .outb = test_outb,
+ .outw = test_outw,
+ .outl = test_outl,
+ .mmio_read = test_mem_read,
+ .mmio_write = test_mem_write,
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ .debug = test_debug,
+#endif
+ .halt = test_halt,
+ .io_window = test_io_window,
+ .try_push_interrupts = test_try_push_interrupts,
+#ifdef KVM_CAP_USER_NMI
+ .push_nmi = test_push_nmi,
+#endif
+ .post_kvm_run = test_post_kvm_run,
+ .pre_kvm_run = test_pre_kvm_run,
+ .shutdown = test_shutdown,
+};
+
+static void load_file(void *mem, const char *fname)
+{
+ int r;
+ int fd;
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(1);
+ }
+ while ((r = read(fd, mem, 4096)) != -1 && r != 0)
+ mem += r;
+ if (r == -1) {
+ perror("read");
+ exit(1);
+ }
+}
+
+static void enter_32(kvm_context_t kvm)
+{
+ struct kvm_regs regs = {
+ .rsp = 0x80000, /* 512KB */
+ .rip = 0x100000, /* 1MB */
+ .rflags = 2,
+ };
+ struct kvm_sregs sregs = {
+ .cs = { 0, -1u, 8, 11, 1, 0, 1, 1, 0, 1, 0, 0 },
+ .ds = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
+ .es = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
+ .fs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
+ .gs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
+ .ss = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 },
+
+ .tr = { 0, 10000, 24, 11, 1, 0, 0, 0, 0, 0, 0, 0 },
+ .ldt = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ .gdt = { 0, 0 },
+ .idt = { 0, 0 },
+ .cr0 = 0x37,
+ .cr3 = 0,
+ .cr4 = 0,
+ .efer = 0,
+ .apic_base = 0,
+ .interrupt_bitmap = { 0 },
+ };
+
+ kvm_set_regs(kvm, 0, &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].id = n;
+ vcpus[n].tid = gettid();
+ vcpu = n;
+ kvm_set_signal_mask(kvm, n, &kernel_sigmask);
+ sem_post(&init_sem);
+}
+
+static void *do_create_vcpu(void *_n)
+{
+ int n = (long)_n;
+ struct kvm_regs regs;
+
+ kvm_create_vcpu(kvm, n);
+ init_vcpu(n);
+ sem_wait(&vcpus[n].sipi_sem);
+ kvm_get_regs(kvm, n, &regs);
+ regs.rip = apic_sipi_addr;
+ kvm_set_regs(kvm, n, &regs);
+ kvm_run(kvm, n, &vcpus[n]);
+ return NULL;
+}
+
+static void start_vcpu(int n)
+{
+ pthread_t thread;
+
+ sem_init(&vcpus[n].sipi_sem, 0, 0);
+ pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n);
+}
+
+static void usage(const char *progname)
+{
+ fprintf(stderr,
+"Usage: %s [OPTIONS] [bootstrap] flatfile\n"
+"KVM test harness.\n"
+"\n"
+" -s, --smp=NUM create a VM with NUM virtual CPUs\n"
+" -p, --protected-mode start VM in protected mode\n"
+" -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n"
+" can be used to change the unit (default: `M')\n"
+" -h, --help display this help screen and exit\n"
+"\n"
+"Report bugs to <kvm@vger.kernel.org>.\n"
+ , progname);
+}
+
+static void sig_ignore(int sig)
+{
+ write(1, "boo\n", 4);
+}
+
+int main(int argc, char **argv)
+{
+ void *vm_mem;
+ int i;
+ const char *sopts = "s:phm:";
+ struct option lopts[] = {
+ { "smp", 1, 0, 's' },
+ { "protected-mode", 0, 0, 'p' },
+ { "memory", 1, 0, 'm' },
+ { "help", 0, 0, 'h' },
+ { 0 },
+ };
+ int opt_ind, ch;
+ bool enter_protected_mode = false;
+ int nb_args;
+ char *endptr;
+
+ while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
+ switch (ch) {
+ case 's':
+ ncpus = atoi(optarg);
+ break;
+ case 'p':
+ enter_protected_mode = true;
+ break;
+ case 'm':
+ memory_size = strtoull(optarg, &endptr, 0);
+ switch (*endptr) {
+ case 'G': case 'g':
+ memory_size <<= 30;
+ break;
+ case '\0':
+ case 'M': case 'm':
+ memory_size <<= 20;
+ break;
+ case 'K': case 'k':
+ memory_size <<= 10;
+ break;
+ default:
+ fprintf(stderr,
+ "Unrecongized memory suffix: %c\n",
+ *endptr);
+ exit(1);
+ }
+ if (memory_size == 0) {
+ fprintf(stderr,
+ "Invalid memory size: 0\n");
+ exit(1);
+ }
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ case '?':
+ default:
+ fprintf(stderr,
+ "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(1);
+ }
+ }
+
+ nb_args = argc - optind;
+ if (nb_args < 1 || nb_args > 2) {
+ fprintf(stderr,
+ "Incorrect number of arguments.\n"
+ "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(1);
+ }
+
+ signal(IPI_SIGNAL, sig_ignore);
+
+ vcpus = calloc(ncpus, sizeof *vcpus);
+ if (!vcpus) {
+ fprintf(stderr, "calloc failed\n");
+ return 1;
+ }
+
+ kvm = kvm_init(&test_callbacks, 0);
+ if (!kvm) {
+ fprintf(stderr, "kvm_init failed\n");
+ return 1;
+ }
+ if (kvm_create(kvm, memory_size, &vm_mem) < 0) {
+ kvm_finalize(kvm);
+ fprintf(stderr, "kvm_create failed\n");
+ return 1;
+ }
+
+ vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1);
+
+ if (enter_protected_mode)
+ enter_32(kvm);
+ else
+ load_file(vm_mem + 0xf0000, argv[optind]);
+
+ if (nb_args > 1)
+ load_file(vm_mem + 0x100000, argv[optind + 1]);
+
+ apic_init();
+ misc_init();
+
+ io_table_register(&pio_table, IRQCHIP_IO_BASE, 0x20, irqchip_io, NULL);
+
+ sem_init(&init_sem, 0, 0);
+ for (i = 0; i < ncpus; ++i)
+ start_vcpu(i);
+ for (i = 0; i < ncpus; ++i)
+ sem_wait(&init_sem);
+
+ kvm_run(kvm, 0, &vcpus[0]);
+
+ return 0;
+}
diff --git a/kvm/user/test/lib/libcflat.h b/kvm/user/test/lib/libcflat.h
new file mode 100644
index 000000000..1f96cb80e
--- /dev/null
+++ b/kvm/user/test/lib/libcflat.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#ifndef __LIBCFLAT_H
+#define __LIBCFLAT_H
+
+#include <stdarg.h>
+
+extern void exit(int code);
+extern void panic(char *fmt, ...);
+
+extern unsigned long strlen(const char *buf);
+extern char *strcat(char *dest, const char *src);
+
+extern int printf(const char *fmt, ...);
+extern int vsnprintf(char *buf, int size, const char *fmt, va_list va);
+
+extern void puts(const char *s);
+
+#endif
diff --git a/kvm/user/test/lib/panic.c b/kvm/user/test/lib/panic.c
new file mode 100644
index 000000000..6e0b29ebe
--- /dev/null
+++ b/kvm/user/test/lib/panic.c
@@ -0,0 +1,13 @@
+#include "libcflat.h"
+
+void panic(char *fmt, ...)
+{
+ va_list va;
+ char buf[2000];
+
+ va_start(va, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, va);
+ va_end(va);
+ puts(buf);
+ exit(-1);
+}
diff --git a/kvm/user/test/lib/powerpc/44x/map.c b/kvm/user/test/lib/powerpc/44x/map.c
new file mode 100644
index 000000000..113434d2f
--- /dev/null
+++ b/kvm/user/test/lib/powerpc/44x/map.c
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#include "libcflat.h"
+
+#define TLB_SIZE 64
+
+extern void tlbwe(unsigned int index,
+ unsigned char tid,
+ unsigned int word0,
+ unsigned int word1,
+ unsigned int word2);
+
+unsigned int next_free_index;
+
+#define PAGE_SHIFT 12
+#define PAGE_MASK (~((1<<PAGE_SHIFT)-1))
+
+#define V (1<<9)
+
+void map(unsigned long vaddr, unsigned long paddr)
+{
+ unsigned int w0, w1, w2;
+
+ /* We don't install exception handlers, so we can't handle TLB misses,
+ * so we can't loop around and overwrite entry 0. */
+ if (next_free_index++ >= TLB_SIZE)
+ panic("TLB overflow");
+
+ w0 = (vaddr & PAGE_MASK) | V;
+ w1 = paddr & PAGE_MASK;
+ w2 = 0x3;
+
+ tlbwe(next_free_index, 0, w0, w1, w2);
+}
diff --git a/kvm/user/test/lib/powerpc/44x/timebase.S b/kvm/user/test/lib/powerpc/44x/timebase.S
new file mode 100644
index 000000000..385904da3
--- /dev/null
+++ b/kvm/user/test/lib/powerpc/44x/timebase.S
@@ -0,0 +1,28 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+/* unsigned long long mftb(void); */
+.global mftb
+mftb:
+ mftbu r5
+ mftbl r4
+ mftbu r3
+ cmpw r3, r5
+ bne mftb
+ blr
diff --git a/kvm/user/test/lib/powerpc/44x/timebase.h b/kvm/user/test/lib/powerpc/44x/timebase.h
new file mode 100644
index 000000000..ce85347bd
--- /dev/null
+++ b/kvm/user/test/lib/powerpc/44x/timebase.h
@@ -0,0 +1,25 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#ifndef __TIMEBASE_H__
+#define __TIMEBASE_H__
+
+unsigned long long mftb(void);
+
+#endif /* __TIMEBASE_H__ */
diff --git a/kvm/user/test/lib/powerpc/44x/tlbwe.S b/kvm/user/test/lib/powerpc/44x/tlbwe.S
new file mode 100644
index 000000000..3790374eb
--- /dev/null
+++ b/kvm/user/test/lib/powerpc/44x/tlbwe.S
@@ -0,0 +1,29 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#define SPRN_MMUCR 0x3b2
+
+/* tlbwe(uint index, uint8_t tid, uint word0, uint word1, uint word2) */
+.global tlbwe
+tlbwe:
+ mtspr SPRN_MMUCR, r4
+ tlbwe r5, r3, 0
+ tlbwe r6, r3, 1
+ tlbwe r7, r3, 2
+ blr
diff --git a/kvm/user/test/lib/powerpc/io.c b/kvm/user/test/lib/powerpc/io.c
new file mode 100644
index 000000000..8bd239521
--- /dev/null
+++ b/kvm/user/test/lib/powerpc/io.c
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#include "libcflat.h"
+
+#define BASE 0xf0000000
+#define _putc ((volatile char *)(BASE))
+#define _exit ((volatile char *)(BASE+1))
+
+void puts(const char *s)
+{
+ while (*s != '\0')
+ *_putc = *s++;
+}
+
+void exit(int code)
+{
+ *_exit = code;
+}
diff --git a/kvm/user/test/lib/printf.c b/kvm/user/test/lib/printf.c
new file mode 100644
index 000000000..3bb9e3d74
--- /dev/null
+++ b/kvm/user/test/lib/printf.c
@@ -0,0 +1,179 @@
+#include "libcflat.h"
+
+typedef struct pstream {
+ char *buffer;
+ int remain;
+ int added;
+} pstream_t;
+
+static void addchar(pstream_t *p, char c)
+{
+ if (p->remain) {
+ *p->buffer++ = c;
+ --p->remain;
+ }
+ ++p->added;
+}
+
+void print_str(pstream_t *p, const char *s)
+{
+ while (*s)
+ addchar(p, *s++);
+}
+
+static char digits[16] = "0123456789abcdef";
+
+void print_int(pstream_t *ps, long long n, int base)
+{
+ char buf[sizeof(long) * 3 + 2], *p = buf;
+ int s = 0, i;
+
+ if (n < 0) {
+ n = -n;
+ s = 1;
+ }
+
+ while (n) {
+ *p++ = digits[n % base];
+ n /= base;
+ }
+
+ if (s)
+ *p++ = '-';
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print_str(ps, buf);
+}
+
+void print_unsigned(pstream_t *ps, unsigned long long n, int base)
+{
+ char buf[sizeof(long) * 3 + 1], *p = buf;
+ int i;
+
+ while (n) {
+ *p++ = digits[n % base];
+ n /= base;
+ }
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print_str(ps, buf);
+}
+
+int vsnprintf(char *buf, int size, const char *fmt, va_list va)
+{
+ pstream_t s;
+
+ s.buffer = buf;
+ s.remain = size - 1;
+ s.added = 0;
+ while (*fmt) {
+ char f = *fmt++;
+ int nlong = 0;
+
+ if (f != '%') {
+ addchar(&s, f);
+ continue;
+ }
+ morefmt:
+ f = *fmt++;
+ switch (f) {
+ case '%':
+ addchar(&s, '%');
+ break;
+ case '\0':
+ --fmt;
+ break;
+ case 'l':
+ ++nlong;
+ goto morefmt;
+ case 'd':
+ switch (nlong) {
+ case 0:
+ print_int(&s, va_arg(va, int), 10);
+ break;
+ case 1:
+ print_int(&s, va_arg(va, long), 10);
+ break;
+ default:
+ print_int(&s, va_arg(va, long long), 10);
+ break;
+ }
+ break;
+ case 'x':
+ switch (nlong) {
+ case 0:
+ print_unsigned(&s, va_arg(va, unsigned), 16);
+ break;
+ case 1:
+ print_unsigned(&s, va_arg(va, unsigned long), 16);
+ break;
+ default:
+ print_unsigned(&s, va_arg(va, unsigned long long), 16);
+ break;
+ }
+ break;
+ case 'p':
+ print_str(&s, "0x");
+ print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
+ break;
+ case 's':
+ print_str(&s, va_arg(va, const char *));
+ break;
+ default:
+ addchar(&s, f);
+ break;
+ }
+ }
+ *s.buffer = 0;
+ ++s.added;
+ return s.added;
+}
+
+
+int snprintf(char *buf, int size, const char *fmt, ...)
+{
+ va_list va;
+ int r;
+
+ va_start(va, fmt);
+ r = vsnprintf(buf, size, fmt, va);
+ va_end(va);
+ return r;
+}
+
+int printf(const char *fmt, ...)
+{
+ va_list va;
+ char buf[2000];
+ int r;
+
+ va_start(va, fmt);
+ r = vsnprintf(buf, sizeof buf, fmt, va);
+ va_end(va);
+ puts(buf);
+ return r;
+}
diff --git a/kvm/user/test/lib/string.c b/kvm/user/test/lib/string.c
new file mode 100644
index 000000000..42be94697
--- /dev/null
+++ b/kvm/user/test/lib/string.c
@@ -0,0 +1,21 @@
+#include "libcflat.h"
+
+unsigned long strlen(const char *buf)
+{
+ unsigned long len = 0;
+
+ while (*buf++)
+ ++len;
+ return len;
+}
+
+char *strcat(char *dest, const char *src)
+{
+ char *p = dest;
+
+ while (*p)
+ ++p;
+ while ((*p++ = *src++) != 0)
+ ;
+ return dest;
+}
diff --git a/kvm/user/test/lib/x86/fake-apic.h b/kvm/user/test/lib/x86/fake-apic.h
new file mode 100644
index 000000000..eed63baef
--- /dev/null
+++ b/kvm/user/test/lib/x86/fake-apic.h
@@ -0,0 +1,14 @@
+#ifndef SILLY_APIC_H
+#define SILLY_APIC_H
+
+#define APIC_BASE 0x1000
+#define APIC_SIZE 0x100
+
+#define APIC_REG_NCPU 0x00
+#define APIC_REG_ID 0x04
+#define APIC_REG_SIPI_ADDR 0x08
+#define APIC_REG_SEND_SIPI 0x0c
+#define APIC_REG_IPI_VECTOR 0x10
+#define APIC_REG_SEND_IPI 0x14
+
+#endif
diff --git a/kvm/user/test/lib/x86/io.c b/kvm/user/test/lib/x86/io.c
new file mode 100644
index 000000000..894f398b1
--- /dev/null
+++ b/kvm/user/test/lib/x86/io.c
@@ -0,0 +1,23 @@
+#include "libcflat.h"
+#include "smp.h"
+
+static struct spinlock lock;
+
+static void print_serial(const char *buf)
+{
+ unsigned long len = strlen(buf);
+
+ asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+}
+
+void puts(const char *s)
+{
+ spin_lock(&lock);
+ print_serial(s);
+ spin_unlock(&lock);
+}
+
+void exit(int code)
+{
+ asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4));
+}
diff --git a/kvm/user/test/lib/x86/smp.c b/kvm/user/test/lib/x86/smp.c
new file mode 100644
index 000000000..92ebada77
--- /dev/null
+++ b/kvm/user/test/lib/x86/smp.c
@@ -0,0 +1,150 @@
+
+#include <libcflat.h>
+#include "smp.h"
+#include "fake-apic.h"
+
+#define IPI_VECTOR 0x20
+
+static int apic_read(int reg)
+{
+ unsigned short port = APIC_BASE + reg;
+ unsigned v;
+
+ asm volatile ("in %1, %0" : "=a"(v) : "d"(port));
+ return v;
+}
+
+static void apic_write(int reg, unsigned v)
+{
+ unsigned short port = APIC_BASE + reg;
+
+ asm volatile ("out %0, %1" : : "a"(v), "d"(port));
+}
+
+static int apic_get_cpu_count()
+{
+ return apic_read(APIC_REG_NCPU);
+}
+
+static int apic_get_id()
+{
+ return apic_read(APIC_REG_ID);
+}
+
+static void apic_set_ipi_vector(int vector)
+{
+ apic_write(APIC_REG_IPI_VECTOR, vector);
+}
+
+static void apic_send_ipi(int cpu)
+{
+ apic_write(APIC_REG_SEND_IPI, cpu);
+}
+
+static struct spinlock ipi_lock;
+static void (*ipi_function)(void *data);
+static void *ipi_data;
+static volatile int ipi_done;
+
+static __attribute__((used)) void ipi()
+{
+ ipi_function(ipi_data);
+ ipi_done = 1;
+}
+
+asm (
+ "ipi_entry: \n"
+ " call ipi \n"
+#ifndef __x86_64__
+ " iret"
+#else
+ " iretq"
+#endif
+ );
+
+
+static void set_ipi_descriptor(void (*ipi_entry)(void))
+{
+ unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2);
+ unsigned short cs;
+ unsigned long ipi = (unsigned long)ipi_entry;
+
+ asm ("mov %%cs, %0" : "=r"(cs));
+ desc[0] = ipi;
+ desc[1] = cs;
+ desc[2] = 0x8e00;
+ desc[3] = ipi >> 16;
+#ifdef __x86_64__
+ desc[4] = ipi >> 32;
+ desc[5] = ipi >> 48;
+ desc[6] = 0;
+ desc[7] = 0;
+#endif
+}
+
+void spin_lock(struct spinlock *lock)
+{
+ int v = 1;
+
+ do {
+ asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v));
+ } while (v);
+ asm volatile ("" : : : "memory");
+}
+
+void spin_unlock(struct spinlock *lock)
+{
+ asm volatile ("" : : : "memory");
+ lock->v = 0;
+}
+
+int cpu_count(void)
+{
+ return apic_get_cpu_count();
+}
+
+int smp_id(void)
+{
+ return apic_get_id();
+}
+
+void on_cpu(int cpu, void (*function)(void *data), void *data)
+{
+ spin_lock(&ipi_lock);
+ if (cpu == apic_get_id())
+ function(data);
+ else {
+ ipi_function = function;
+ ipi_data = data;
+ apic_send_ipi(cpu);
+ while (!ipi_done)
+ ;
+ ipi_done = 0;
+ }
+ spin_unlock(&ipi_lock);
+}
+
+static void (*smp_main_func)(void);
+static volatile int smp_main_running;
+
+asm ("smp_init_entry: \n"
+ "incl smp_main_running \n"
+ "sti \n"
+ "call *smp_main_func");
+
+void smp_init(void (*smp_main)(void))
+{
+ int i;
+ void smp_init_entry(void);
+ void ipi_entry(void);
+
+ apic_set_ipi_vector(IPI_VECTOR);
+ set_ipi_descriptor(smp_init_entry);
+ smp_main_func = smp_main;
+ for (i = 1; i < cpu_count(); ++i) {
+ apic_send_ipi(i);
+ while (smp_main_running < i)
+ ;
+ }
+ set_ipi_descriptor(ipi_entry);
+}
diff --git a/kvm/user/test/lib/x86/smp.h b/kvm/user/test/lib/x86/smp.h
new file mode 100644
index 000000000..bcf76a357
--- /dev/null
+++ b/kvm/user/test/lib/x86/smp.h
@@ -0,0 +1,16 @@
+#ifndef __SMP_H
+#define __SMP_H
+
+struct spinlock {
+ int v;
+};
+
+void smp_init(void (*smp_main)(void));
+
+int cpu_count(void);
+int smp_id(void);
+void on_cpu(int cpu, void (*function)(void *data), void *data);
+void spin_lock(struct spinlock *lock);
+void spin_unlock(struct spinlock *lock);
+
+#endif
diff --git a/kvm/user/test/powerpc/44x/tlbsx.S b/kvm/user/test/powerpc/44x/tlbsx.S
new file mode 100644
index 000000000..b15874b18
--- /dev/null
+++ b/kvm/user/test/powerpc/44x/tlbsx.S
@@ -0,0 +1,33 @@
+#define SPRN_MMUCR 0x3b2
+
+#define TLBWORD0 0x10000210
+#define TLBWORD1 0x10000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 23
+
+ lis r4, TLBWORD0@h
+ ori r4, r4, TLBWORD0@l
+ tlbwe r4, r3, 0
+
+ lis r4, TLBWORD1@h
+ ori r4, r4, TLBWORD1@l
+ tlbwe r4, r3, 1
+
+ lis r4, TLBWORD2@h
+ ori r4, r4, TLBWORD2@l
+ tlbwe r4, r3, 2
+
+ lis r4, 0x1000
+ tlbsx r5, r4, r0
+ cmpwi r5, 23
+ beq good
+ trap
+
+good:
+ b .
diff --git a/kvm/user/test/powerpc/44x/tlbwe.S b/kvm/user/test/powerpc/44x/tlbwe.S
new file mode 100644
index 000000000..ec6ef5c57
--- /dev/null
+++ b/kvm/user/test/powerpc/44x/tlbwe.S
@@ -0,0 +1,27 @@
+#define SPRN_MMUCR 0x3b2
+
+/* Create a mapping at 4MB */
+#define TLBWORD0 0x00400210
+#define TLBWORD1 0x00400000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 23
+
+ lis r4, TLBWORD0@h
+ ori r4, r4, TLBWORD0@l
+ tlbwe r4, r3, 0
+
+ lis r4, TLBWORD1@h
+ ori r4, r4, TLBWORD1@l
+ tlbwe r4, r3, 1
+
+ lis r4, TLBWORD2@h
+ ori r4, r4, TLBWORD2@l
+ tlbwe r4, r3, 2
+
+ b .
diff --git a/kvm/user/test/powerpc/44x/tlbwe_16KB.S b/kvm/user/test/powerpc/44x/tlbwe_16KB.S
new file mode 100644
index 000000000..1bd10bf17
--- /dev/null
+++ b/kvm/user/test/powerpc/44x/tlbwe_16KB.S
@@ -0,0 +1,35 @@
+#define SPRN_MMUCR 0x3b2
+
+/* 16KB mapping at 4MB */
+#define TLBWORD0 0x00400220
+#define TLBWORD1 0x00400000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 5
+
+ lis r4, TLBWORD0@h
+ ori r4, r4, TLBWORD0@l
+ tlbwe r4, r3, 0
+
+ lis r4, TLBWORD1@h
+ ori r4, r4, TLBWORD1@l
+ tlbwe r4, r3, 1
+
+ lis r4, TLBWORD2@h
+ ori r4, r4, TLBWORD2@l
+ tlbwe r4, r3, 2
+
+ /* load from 4MB */
+ lis r3, 0x0040
+ lwz r4, 0(r3)
+
+ /* load from 4MB+8KB */
+ ori r3, r3, 0x2000
+ lwz r4, 0(r3)
+
+ b .
diff --git a/kvm/user/test/powerpc/44x/tlbwe_hole.S b/kvm/user/test/powerpc/44x/tlbwe_hole.S
new file mode 100644
index 000000000..5efd30357
--- /dev/null
+++ b/kvm/user/test/powerpc/44x/tlbwe_hole.S
@@ -0,0 +1,27 @@
+#define SPRN_MMUCR 0x3b2
+
+/* Try to map real address 1GB. */
+#define TLBWORD0 0x40000210
+#define TLBWORD1 0x40000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 23
+
+ lis r4, TLBWORD0@h
+ ori r4, r4, TLBWORD0@l
+ tlbwe r4, r3, 0
+
+ lis r4, TLBWORD1@h
+ ori r4, r4, TLBWORD1@l
+ tlbwe r4, r3, 1
+
+ lis r4, TLBWORD2@h
+ ori r4, r4, TLBWORD2@l
+ tlbwe r4, r3, 2
+
+ b .
diff --git a/kvm/user/test/powerpc/cstart.S b/kvm/user/test/powerpc/cstart.S
new file mode 100644
index 000000000..70a0e9fcd
--- /dev/null
+++ b/kvm/user/test/powerpc/cstart.S
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#define OUTPUT_VADDR 0xf0000000
+#define OUTPUT_PADDR 0xf0000000
+
+.globl _start
+_start:
+ /* In the future we might need to assign a stack and zero BSS here. */
+
+ /* Map the debug page 1:1. */
+ lis r3, OUTPUT_VADDR@h
+ ori r3, r3, OUTPUT_VADDR@l
+ lis r4, OUTPUT_PADDR@h
+ ori r4, r4, OUTPUT_PADDR@l
+ bl map
+
+ /* Call main() and pass return code to exit(). */
+ bl main
+ bl exit
+
+ b .
diff --git a/kvm/user/test/powerpc/exit.c b/kvm/user/test/powerpc/exit.c
new file mode 100644
index 000000000..804ee04d9
--- /dev/null
+++ b/kvm/user/test/powerpc/exit.c
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+int main(void)
+{
+ return 1;
+}
diff --git a/kvm/user/test/powerpc/helloworld.c b/kvm/user/test/powerpc/helloworld.c
new file mode 100644
index 000000000..f8630f7c5
--- /dev/null
+++ b/kvm/user/test/powerpc/helloworld.c
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Deepa Srinivasan <deepas@us.ibm.com>
+ */
+
+#include "libcflat.h"
+
+int main()
+{
+ printf("Hello World\n");
+
+ return 1;
+}
diff --git a/kvm/user/test/powerpc/io.S b/kvm/user/test/powerpc/io.S
new file mode 100644
index 000000000..97567cb6c
--- /dev/null
+++ b/kvm/user/test/powerpc/io.S
@@ -0,0 +1,32 @@
+#define SPRN_MMUCR 0x3b2
+
+#define TLBWORD0 0xf0000210
+#define TLBWORD1 0xf0000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 2
+
+ lis r4, TLBWORD0@h
+ ori r4, r4, TLBWORD0@l
+ tlbwe r4, r3, 0
+
+ lis r4, TLBWORD1@h
+ ori r4, r4, TLBWORD1@l
+ tlbwe r4, r3, 1
+
+ lis r4, TLBWORD2@h
+ ori r4, r4, TLBWORD2@l
+ tlbwe r4, r3, 2
+
+ lis r3, 0xf000
+ lis r4, 0x1234
+ ori r4, r4, 0x5678
+ stb r4, 0(r3)
+ lbz r5, 0(r3)
+
+ b .
diff --git a/kvm/user/test/powerpc/spin.S b/kvm/user/test/powerpc/spin.S
new file mode 100644
index 000000000..4406641c2
--- /dev/null
+++ b/kvm/user/test/powerpc/spin.S
@@ -0,0 +1,4 @@
+
+.global _start
+_start:
+ b .
diff --git a/kvm/user/test/powerpc/sprg.S b/kvm/user/test/powerpc/sprg.S
new file mode 100644
index 000000000..d0414a480
--- /dev/null
+++ b/kvm/user/test/powerpc/sprg.S
@@ -0,0 +1,7 @@
+
+.global _start
+_start:
+ li r3, 42
+ mtsprg 0, r3
+ mfsprg r4, 0
+ b .
diff --git a/kvm/user/test/x86/access.c b/kvm/user/test/x86/access.c
new file mode 100644
index 000000000..0e615975a
--- /dev/null
+++ b/kvm/user/test/x86/access.c
@@ -0,0 +1,580 @@
+
+#include "libcflat.h"
+#include "smp.h"
+
+#define true 1
+#define false 0
+
+typedef unsigned long pt_element_t;
+
+#define PAGE_SIZE ((pt_element_t)4096)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK))
+#define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21))
+
+#define PT_PRESENT_MASK ((pt_element_t)1 << 0)
+#define PT_WRITABLE_MASK ((pt_element_t)1 << 1)
+#define PT_USER_MASK ((pt_element_t)1 << 2)
+#define PT_ACCESSED_MASK ((pt_element_t)1 << 5)
+#define PT_DIRTY_MASK ((pt_element_t)1 << 6)
+#define PT_PSE_MASK ((pt_element_t)1 << 7)
+#define PT_NX_MASK ((pt_element_t)1 << 63)
+
+#define CR0_WP_MASK (1UL << 16)
+
+#define PFERR_PRESENT_MASK (1U << 0)
+#define PFERR_WRITE_MASK (1U << 1)
+#define PFERR_USER_MASK (1U << 2)
+#define PFERR_RESERVED_MASK (1U << 3)
+#define PFERR_FETCH_MASK (1U << 4)
+
+#define MSR_EFER 0xc0000080
+#define EFER_NX_MASK (1ull << 11)
+
+/*
+ * page table access check tests
+ */
+
+enum {
+ AC_PTE_PRESENT,
+ AC_PTE_WRITABLE,
+ AC_PTE_USER,
+ AC_PTE_ACCESSED,
+ AC_PTE_DIRTY,
+ AC_PTE_NX,
+
+ AC_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/apic.c b/kvm/user/test/x86/apic.c
new file mode 100644
index 000000000..9c6205b4d
--- /dev/null
+++ b/kvm/user/test/x86/apic.c
@@ -0,0 +1,351 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "vm.h"
+
+static void *g_apic;
+static void *g_ioapic;
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+typedef unsigned long ulong;
+
+typedef struct {
+ unsigned short offset0;
+ unsigned short selector;
+ unsigned short ist : 3;
+ unsigned short : 5;
+ unsigned short type : 4;
+ unsigned short : 1;
+ unsigned short dpl : 2;
+ unsigned short p : 1;
+ unsigned short offset1;
+#ifdef __x86_64__
+ unsigned offset2;
+ unsigned reserved;
+#endif
+} idt_entry_t;
+
+typedef struct {
+ ulong rflags;
+ ulong cs;
+ ulong rip;
+ ulong func;
+ ulong regs[sizeof(ulong)*2];
+} isr_regs_t;
+
+#ifdef __x86_64__
+# define R "r"
+#else
+# define R "e"
+#endif
+
+extern char isr_entry_point[];
+
+asm (
+ "isr_entry_point: \n"
+#ifdef __x86_64__
+ "push %r15 \n\t"
+ "push %r14 \n\t"
+ "push %r13 \n\t"
+ "push %r12 \n\t"
+ "push %r11 \n\t"
+ "push %r10 \n\t"
+ "push %r9 \n\t"
+ "push %r8 \n\t"
+#endif
+ "push %"R"di \n\t"
+ "push %"R"si \n\t"
+ "push %"R"bp \n\t"
+ "push %"R"sp \n\t"
+ "push %"R"bx \n\t"
+ "push %"R"dx \n\t"
+ "push %"R"cx \n\t"
+ "push %"R"ax \n\t"
+#ifdef __x86_64__
+ "mov %rsp, %rdi \n\t"
+ "callq *8*16(%rsp) \n\t"
+#else
+ "push %esp \n\t"
+ "calll *4+4*8(%esp) \n\t"
+ "add $4, %esp \n\t"
+#endif
+ "pop %"R"ax \n\t"
+ "pop %"R"cx \n\t"
+ "pop %"R"dx \n\t"
+ "pop %"R"bx \n\t"
+ "pop %"R"bp \n\t"
+ "pop %"R"bp \n\t"
+ "pop %"R"si \n\t"
+ "pop %"R"di \n\t"
+#ifdef __x86_64__
+ "pop %r8 \n\t"
+ "pop %r9 \n\t"
+ "pop %r10 \n\t"
+ "pop %r11 \n\t"
+ "pop %r12 \n\t"
+ "pop %r13 \n\t"
+ "pop %r14 \n\t"
+ "pop %r15 \n\t"
+#endif
+#ifdef __x86_64__
+ "add $8, %rsp \n\t"
+ "iretq \n\t"
+#else
+ "add $4, %esp \n\t"
+ "iretl \n\t"
+#endif
+ );
+
+static idt_entry_t idt[256];
+
+static int g_fail;
+static int g_tests;
+
+static void report(const char *msg, int pass)
+{
+ ++g_tests;
+ printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+ if (!pass)
+ ++g_fail;
+}
+
+static u32 apic_read(unsigned reg)
+{
+ return *(volatile u32 *)(g_apic + reg);
+}
+
+static void apic_write(unsigned reg, u32 val)
+{
+ *(volatile u32 *)(g_apic + reg) = val;
+}
+
+static void test_lapic_existence(void)
+{
+ u32 lvr;
+
+ lvr = apic_read(APIC_LVR);
+ printf("apic version: %x\n", lvr);
+ report("apic existence", (u16)lvr == 0x14);
+}
+
+static u16 read_cs(void)
+{
+ u16 v;
+
+ asm("mov %%cs, %0" : "=rm"(v));
+ return v;
+}
+
+static void init_idt(void)
+{
+ struct {
+ u16 limit;
+ ulong idt;
+ } __attribute__((packed)) idt_ptr = {
+ sizeof(idt_entry_t) * 256 - 1,
+ (ulong)&idt,
+ };
+
+ asm volatile("lidt %0" : : "m"(idt_ptr));
+}
+
+static void set_idt_entry(unsigned vec, void (*func)(isr_regs_t *regs))
+{
+ u8 *thunk = vmalloc(50);
+ ulong ptr = (ulong)thunk;
+ idt_entry_t ent = {
+ .offset0 = ptr,
+ .selector = read_cs(),
+ .ist = 0,
+ .type = 14,
+ .dpl = 0,
+ .p = 1,
+ .offset1 = ptr >> 16,
+#ifdef __x86_64__
+ .offset2 = ptr >> 32,
+#endif
+ };
+#ifdef __x86_64__
+ /* sub $8, %rsp */
+ *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08;
+ /* mov $func_low, %(rsp) */
+ *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24;
+ *(u32 *)thunk = (ulong)func; thunk += 4;
+ /* mov $func_high, %(rsp+4) */
+ *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04;
+ *(u32 *)thunk = (ulong)func >> 32; thunk += 4;
+ /* jmp isr_entry_point */
+ *thunk ++ = 0xe9;
+ *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#else
+ /* push $func */
+ *thunk++ = 0x68;
+ *(u32 *)thunk = (ulong)func;
+ /* jmp isr_entry_point */
+ *thunk ++ = 0xe9;
+ *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#endif
+ idt[vec] = ent;
+}
+
+static void irq_disable(void)
+{
+ asm volatile("cli");
+}
+
+static void irq_enable(void)
+{
+ asm volatile("sti");
+}
+
+static void eoi(void)
+{
+ apic_write(APIC_EOI, 0);
+}
+
+static int ipi_count;
+
+static void self_ipi_isr(isr_regs_t *regs)
+{
+ ++ipi_count;
+ eoi();
+}
+
+static void test_self_ipi(void)
+{
+ int vec = 0xf1;
+
+ set_idt_entry(vec, self_ipi_isr);
+ irq_enable();
+ apic_write(APIC_ICR,
+ APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec);
+ asm volatile ("nop");
+ report("self ipi", ipi_count == 1);
+}
+
+static void ioapic_write_reg(unsigned reg, u32 value)
+{
+ *(volatile u32 *)g_ioapic = reg;
+ *(volatile u32 *)(g_ioapic + 0x10) = value;
+}
+
+typedef struct {
+ u8 vector;
+ u8 delivery_mode:3;
+ u8 dest_mode:1;
+ u8 delivery_status:1;
+ u8 polarity:1;
+ u8 remote_irr:1;
+ u8 trig_mode:1;
+ u8 mask:1;
+ u8 reserve:7;
+ u8 reserved[4];
+ u8 dest_id;
+} ioapic_redir_entry_t;
+
+static void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e)
+{
+ ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]);
+ ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]);
+}
+
+static void set_ioapic_redir(unsigned line, unsigned vec)
+{
+ ioapic_redir_entry_t e = {
+ .vector = vec,
+ .delivery_mode = 0,
+ .trig_mode = 0,
+ };
+
+ ioapic_write_redir(line, e);
+}
+
+static void set_irq_line(unsigned line, int val)
+{
+ asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
+}
+
+static void toggle_irq_line(unsigned line)
+{
+ set_irq_line(line, 1);
+ set_irq_line(line, 0);
+}
+
+static int g_isr_77;
+
+static void ioapic_isr_77(isr_regs_t *regs)
+{
+ ++g_isr_77;
+ eoi();
+}
+
+static void test_ioapic_intr(void)
+{
+ set_idt_entry(0x77, ioapic_isr_77);
+ set_ioapic_redir(0x10, 0x77);
+ toggle_irq_line(0x10);
+ asm volatile ("nop");
+ report("ioapic interrupt", g_isr_77 == 1);
+}
+
+static int g_78, g_66, g_66_after_78;
+static ulong g_66_rip, g_78_rip;
+
+static void ioapic_isr_78(isr_regs_t *regs)
+{
+ ++g_78;
+ g_78_rip = regs->rip;
+ eoi();
+}
+
+static void ioapic_isr_66(isr_regs_t *regs)
+{
+ ++g_66;
+ if (g_78)
+ ++g_66_after_78;
+ g_66_rip = regs->rip;
+ eoi();
+}
+
+static void test_ioapic_simultaneous(void)
+{
+ set_idt_entry(0x78, ioapic_isr_78);
+ set_idt_entry(0x66, ioapic_isr_66);
+ set_ioapic_redir(0x10, 0x78);
+ set_ioapic_redir(0x11, 0x66);
+ irq_disable();
+ toggle_irq_line(0x11);
+ toggle_irq_line(0x10);
+ irq_enable();
+ asm volatile ("nop");
+ report("ioapic simultaneous interrupt",
+ g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
+}
+
+static void enable_apic(void)
+{
+ apic_write(0xf0, 0x1ff); /* spurious vector register */
+}
+
+int main()
+{
+ setup_vm();
+
+ g_apic = vmap(0xfee00000, 0x1000);
+ g_ioapic = vmap(0xfec00000, 0x1000);
+
+ test_lapic_existence();
+
+ enable_apic();
+ init_idt();
+
+ test_self_ipi();
+
+ test_ioapic_intr();
+ test_ioapic_simultaneous();
+
+ printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+ return g_fail != 0;
+}
diff --git a/kvm/user/test/x86/apic.h b/kvm/user/test/x86/apic.h
new file mode 100644
index 000000000..c061e3d4a
--- /dev/null
+++ b/kvm/user/test/x86/apic.h
@@ -0,0 +1,133 @@
+#ifndef _ASM_X86_APICDEF_H
+#define _ASM_X86_APICDEF_H
+
+/*
+ * Constants for various Intel APICs. (local APIC, IOAPIC, etc.)
+ *
+ * Alan Cox <Alan.Cox@linux.org>, 1995.
+ * Ingo Molnar <mingo@redhat.com>, 1999, 2000
+ */
+
+#define APIC_DEFAULT_PHYS_BASE 0xfee00000
+
+#define APIC_ID 0x20
+
+#define APIC_LVR 0x30
+#define APIC_LVR_MASK 0xFF00FF
+#define GET_APIC_VERSION(x) ((x) & 0xFFu)
+#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu)
+#ifdef CONFIG_X86_32
+# define APIC_INTEGRATED(x) ((x) & 0xF0u)
+#else
+# define APIC_INTEGRATED(x) (1)
+#endif
+#define APIC_XAPIC(x) ((x) >= 0x14)
+#define APIC_TASKPRI 0x80
+#define APIC_TPRI_MASK 0xFFu
+#define APIC_ARBPRI 0x90
+#define APIC_ARBPRI_MASK 0xFFu
+#define APIC_PROCPRI 0xA0
+#define APIC_EOI 0xB0
+#define APIC_EIO_ACK 0x0
+#define APIC_RRR 0xC0
+#define APIC_LDR 0xD0
+#define APIC_LDR_MASK (0xFFu << 24)
+#define GET_APIC_LOGICAL_ID(x) (((x) >> 24) & 0xFFu)
+#define SET_APIC_LOGICAL_ID(x) (((x) << 24))
+#define APIC_ALL_CPUS 0xFFu
+#define APIC_DFR 0xE0
+#define APIC_DFR_CLUSTER 0x0FFFFFFFul
+#define APIC_DFR_FLAT 0xFFFFFFFFul
+#define APIC_SPIV 0xF0
+#define APIC_SPIV_FOCUS_DISABLED (1 << 9)
+#define APIC_SPIV_APIC_ENABLED (1 << 8)
+#define APIC_ISR 0x100
+#define APIC_ISR_NR 0x8 /* Number of 32 bit ISR registers. */
+#define APIC_TMR 0x180
+#define APIC_IRR 0x200
+#define APIC_ESR 0x280
+#define APIC_ESR_SEND_CS 0x00001
+#define APIC_ESR_RECV_CS 0x00002
+#define APIC_ESR_SEND_ACC 0x00004
+#define APIC_ESR_RECV_ACC 0x00008
+#define APIC_ESR_SENDILL 0x00020
+#define APIC_ESR_RECVILL 0x00040
+#define APIC_ESR_ILLREGA 0x00080
+#define APIC_ICR 0x300
+#define APIC_DEST_SELF 0x40000
+#define APIC_DEST_ALLINC 0x80000
+#define APIC_DEST_ALLBUT 0xC0000
+#define APIC_ICR_RR_MASK 0x30000
+#define APIC_ICR_RR_INVALID 0x00000
+#define APIC_ICR_RR_INPROG 0x10000
+#define APIC_ICR_RR_VALID 0x20000
+#define APIC_INT_LEVELTRIG 0x08000
+#define APIC_INT_ASSERT 0x04000
+#define APIC_ICR_BUSY 0x01000
+#define APIC_DEST_LOGICAL 0x00800
+#define APIC_DEST_PHYSICAL 0x00000
+#define APIC_DM_FIXED 0x00000
+#define APIC_DM_LOWEST 0x00100
+#define APIC_DM_SMI 0x00200
+#define APIC_DM_REMRD 0x00300
+#define APIC_DM_NMI 0x00400
+#define APIC_DM_INIT 0x00500
+#define APIC_DM_STARTUP 0x00600
+#define APIC_DM_EXTINT 0x00700
+#define APIC_VECTOR_MASK 0x000FF
+#define APIC_ICR2 0x310
+#define GET_APIC_DEST_FIELD(x) (((x) >> 24) & 0xFF)
+#define SET_APIC_DEST_FIELD(x) ((x) << 24)
+#define APIC_LVTT 0x320
+#define APIC_LVTTHMR 0x330
+#define APIC_LVTPC 0x340
+#define APIC_LVT0 0x350
+#define APIC_LVT_TIMER_BASE_MASK (0x3 << 18)
+#define GET_APIC_TIMER_BASE(x) (((x) >> 18) & 0x3)
+#define SET_APIC_TIMER_BASE(x) (((x) << 18))
+#define APIC_TIMER_BASE_CLKIN 0x0
+#define APIC_TIMER_BASE_TMBASE 0x1
+#define APIC_TIMER_BASE_DIV 0x2
+#define APIC_LVT_TIMER_PERIODIC (1 << 17)
+#define APIC_LVT_MASKED (1 << 16)
+#define APIC_LVT_LEVEL_TRIGGER (1 << 15)
+#define APIC_LVT_REMOTE_IRR (1 << 14)
+#define APIC_INPUT_POLARITY (1 << 13)
+#define APIC_SEND_PENDING (1 << 12)
+#define APIC_MODE_MASK 0x700
+#define GET_APIC_DELIVERY_MODE(x) (((x) >> 8) & 0x7)
+#define SET_APIC_DELIVERY_MODE(x, y) (((x) & ~0x700) | ((y) << 8))
+#define APIC_MODE_FIXED 0x0
+#define APIC_MODE_NMI 0x4
+#define APIC_MODE_EXTINT 0x7
+#define APIC_LVT1 0x360
+#define APIC_LVTERR 0x370
+#define APIC_TMICT 0x380
+#define APIC_TMCCT 0x390
+#define APIC_TDCR 0x3E0
+#define APIC_SELF_IPI 0x3F0
+#define APIC_TDR_DIV_TMBASE (1 << 2)
+#define APIC_TDR_DIV_1 0xB
+#define APIC_TDR_DIV_2 0x0
+#define APIC_TDR_DIV_4 0x1
+#define APIC_TDR_DIV_8 0x2
+#define APIC_TDR_DIV_16 0x3
+#define APIC_TDR_DIV_32 0x8
+#define APIC_TDR_DIV_64 0x9
+#define APIC_TDR_DIV_128 0xA
+#define APIC_EILVT0 0x500
+#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */
+#define APIC_EILVT_NR_AMD_10H 4
+#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF)
+#define APIC_EILVT_MSG_FIX 0x0
+#define APIC_EILVT_MSG_SMI 0x2
+#define APIC_EILVT_MSG_NMI 0x4
+#define APIC_EILVT_MSG_EXT 0x7
+#define APIC_EILVT_MASKED (1 << 16)
+#define APIC_EILVT1 0x510
+#define APIC_EILVT2 0x520
+#define APIC_EILVT3 0x530
+
+#define APIC_BASE_MSR 0x800
+
+#endif /* _ASM_X86_APICDEF_H */
diff --git a/kvm/user/test/x86/bootstrap.S b/kvm/user/test/x86/bootstrap.S
new file mode 100644
index 000000000..e32fea90c
--- /dev/null
+++ b/kvm/user/test/x86/bootstrap.S
@@ -0,0 +1,137 @@
+/*
+ * minimal bootstrap to set up flat 32-bit protected mode
+ */
+
+#include "fake-apic.h"
+
+bstart = 0xf0000
+
+.code16
+
+stack_top = 0x1000
+cpu_up = 0x1000
+cpu_up_pmode = 0x1004
+
+pmode_stack_start = 0x10000
+pmode_stack_shift = 16
+pmode_stack_size = (1 << pmode_stack_shift)
+
+ipi_vec = 0xf0
+
+start:
+ mov $stack_top, %sp
+ call smp_init
+
+ cs lidtl idt_desc
+ cs lgdtl gdt_desc
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ ljmpl $8, $pmode + bstart
+
+smp_init:
+ mov $ipi_vec, %eax
+ mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx
+ out %eax, %dx
+ movw $ap_switch_to_pmode, ipi_vec*4
+ movw %cs, %ax
+ mov %ax, ipi_vec*4+2
+ mov $sipi, %eax
+ mov $(APIC_BASE + APIC_REG_SIPI_ADDR), %dx
+ outl %eax, %dx
+ mov $(APIC_BASE + APIC_REG_NCPU), %dx
+ inl %dx, %eax
+ mov %eax, %ecx
+ mov $1, %esi
+smp_loop:
+ cmp %esi, %ecx
+ jbe smp_done
+ mov %esi, %eax
+ mov $(APIC_BASE + APIC_REG_SEND_SIPI), %dx
+ outl %eax, %dx
+wait_for_cpu:
+ cmp cpu_up, %esi
+ jne wait_for_cpu
+ mov %esi, %eax
+ mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx
+ out %eax, %dx
+wait_for_cpu_pmode:
+ cmp cpu_up_pmode, %esi
+ jne wait_for_cpu_pmode
+
+ inc %esi
+ jmp smp_loop
+smp_done:
+ ret
+
+sipi:
+ mov $(APIC_BASE + APIC_REG_ID), %dx
+ inl %dx, %eax
+ mov %eax, cpu_up
+ shl $12, %eax
+ addl $stack_top, %eax
+ movl %eax, %esp
+ sti
+ nop
+1: hlt
+ jmp 1b
+
+ap_switch_to_pmode:
+ cs lidtl idt_desc
+ cs lgdtl gdt_desc
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ ljmpl $8, $ap_pmode + bstart
+
+.code32
+ap_pmode:
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $(APIC_BASE + APIC_REG_ID), %dx
+ in %dx, %eax
+ mov %eax, cpu_up_pmode
+ shl $pmode_stack_shift, %eax
+ lea pmode_stack_start + pmode_stack_size(%eax), %esp
+ sti
+ nop
+ap_pmode_wait:
+ hlt
+ jmp ap_pmode_wait
+
+pmode:
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $pmode_stack_start + pmode_stack_size, %esp
+ ljmp $8, $0x100000
+
+.align 16
+
+idt_desc:
+ .word 8*256-1
+ .long 0
+
+gdt_desc:
+ .word gdt_end - gdt - 1
+ .long gdt + bstart
+
+.align 16
+
+gdt:
+ .quad 0
+ .quad 0x00cf9b000000ffff // flat 32-bit code segment
+ .quad 0x00cf93000000ffff // flat 32-bit data segment
+gdt_end:
+
+. = 0xfff0
+ .code16
+ ljmp $0xf000, $start
+.align 65536
diff --git a/kvm/user/test/x86/cstart.S b/kvm/user/test/x86/cstart.S
new file mode 100644
index 000000000..69a626261
--- /dev/null
+++ b/kvm/user/test/x86/cstart.S
@@ -0,0 +1,10 @@
+
+
+.bss
+
+.section .init
+ call main
+ push %eax
+ call exit
+
+
diff --git a/kvm/user/test/x86/cstart64.S b/kvm/user/test/x86/cstart64.S
new file mode 100644
index 000000000..432a3dc87
--- /dev/null
+++ b/kvm/user/test/x86/cstart64.S
@@ -0,0 +1,168 @@
+
+#include "fake-apic.h"
+
+boot_idt = 0
+
+ipi_vector = 0x20
+
+max_cpus = 4
+
+.bss
+
+ . = . + 4096 * max_cpus
+ .align 16
+stacktop:
+
+ . = . + 4096
+ .align 16
+ring0stacktop:
+
+.data
+
+.align 4096
+ptl2:
+i = 0
+ .rept 512
+ .quad 0x1e7 | (i << 21)
+ i = i + 1
+ .endr
+
+.align 4096
+ptl3:
+ .quad ptl2 + 7
+
+.align 4096
+ptl4:
+ .quad ptl3 + 7
+
+.align 4096
+
+gdt64_desc:
+ .word gdt64_end - gdt64 - 1
+ .quad gdt64
+
+gdt64:
+ .quad 0
+ .quad 0x00af9b000000ffff // 64-bit code segment
+ .quad 0x00cf93000000ffff // 64-bit data segment
+ .quad 0x00affb000000ffff // 64-bit code segment (user)
+ .quad 0x00cff3000000ffff // 64-bit data segment (user)
+tss_descr:
+ .rept max_cpus
+ .quad 0x000089000000ffff // 64-bit avail tss
+ .quad 0 // tss high addr
+ .endr
+gdt64_end:
+
+i = 0
+tss:
+ .rept max_cpus
+ .long 0
+ .quad ring0stacktop - i * 4096
+ .quad 0, 0, 0
+ .quad 0, 0, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0
+i = i + 1
+ .endr
+tss_end:
+
+.section .init
+
+.code32
+ call prepare_64
+ jmpl $8, $start64
+
+prepare_64:
+ lgdt gdt64_desc
+
+ mov %cr4, %eax
+ bts $5, %eax // pae
+ mov %eax, %cr4
+
+ mov $ptl4, %eax
+ mov %eax, %cr3
+
+efer = 0xc0000080
+ mov $efer, %ecx
+ rdmsr
+ bts $8, %eax
+ wrmsr
+
+ mov %cr0, %eax
+ bts $0, %eax
+ bts $31, %eax
+ mov %eax, %cr0
+ ret
+
+
+smp_init_ipi:
+ call prepare_64
+ jmpl $8, $ap_start64
+
+.code64
+ap_start64:
+ call load_tss
+ sti
+ nop
+
+1: hlt
+ jmp 1b
+
+start64:
+ call load_tss
+ call smp_init
+ call main
+ mov %eax, %edi
+ call exit
+
+load_tss:
+ mov $0, %eax
+ mov %ax, %ss
+ mov $(APIC_BASE + APIC_REG_ID), %dx
+ in %dx, %eax
+ mov %eax, %ebx
+ shl $4, %ebx
+ mov $((tss_end - tss) / max_cpus), %edx
+ imul %edx
+ add $tss, %rax
+ mov %ax, tss_descr+2(%rbx)
+ shr $16, %rax
+ mov %al, tss_descr+4(%rbx)
+ shr $8, %rax
+ mov %al, tss_descr+7(%rbx)
+ shr $8, %rax
+ mov %eax, tss_descr+8(%rbx)
+ lea tss_descr-gdt64(%rbx), %rax
+ ltr %ax
+ ret
+
+smp_init:
+ lea boot_idt + ipi_vector * 8, %rdi
+ mov $smp_init_ipi, %eax
+ mov %ax, (%rdi)
+ mov %cs, %ax
+ mov %ax, 2(%rdi)
+ movw $0x8e00, 4(%rdi)
+ shr $16, %eax
+ mov %ax, 6(%rdi)
+
+ mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx
+ mov $ipi_vector, %eax
+ out %eax, %dx
+
+ mov $(APIC_BASE + APIC_REG_NCPU), %dx
+ in %dx, %eax
+ mov %eax, %ecx
+ mov $1, %esi
+smp_loop:
+ cmp %esi, %ecx
+ je smp_init_done
+
+ mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx
+ mov %esi, %eax
+ out %eax, %dx
+
+ inc %esi
+ jmp smp_loop
+smp_init_done:
+ ret
diff --git a/kvm/user/test/x86/emulator.c b/kvm/user/test/x86/emulator.c
new file mode 100644
index 000000000..bbe55a720
--- /dev/null
+++ b/kvm/user/test/x86/emulator.c
@@ -0,0 +1,259 @@
+#include "ioram.h"
+#include "vm.h"
+#include "libcflat.h"
+
+#define memset __builtin_memset
+
+int fails, tests;
+
+void report(const char *name, int result)
+{
+ ++tests;
+ if (result)
+ printf("PASS: %s\n", name);
+ else {
+ printf("FAIL: %s\n", name);
+ ++fails;
+ }
+}
+
+void test_cmps(void *mem)
+{
+ unsigned char *m1 = mem, *m2 = mem + 1024;
+ unsigned char m3[1024];
+ void *rsi, *rdi;
+ long rcx, tmp;
+
+ for (int i = 0; i < 100; ++i)
+ m1[i] = m2[i] = m3[i] = i;
+ for (int i = 100; i < 200; ++i)
+ m1[i] = (m3[i] = m2[i] = i) + 1;
+
+ rsi = m1; rdi = m3; rcx = 30;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsb"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsb (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
+
+ rsi = m1; rdi = m3; rcx = 15;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsw"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsw (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
+
+ rsi = m1; rdi = m3; rcx = 7;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsl"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpll (1)", rcx == 0 && rsi == m1 + 28 && rdi == m3 + 28);
+
+ rsi = m1; rdi = m3; rcx = 4;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsq"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsq (1)", rcx == 0 && rsi == m1 + 32 && rdi == m3 + 32);
+
+ rsi = m1; rdi = m3; rcx = 130;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsb"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsb (2)",
+ rcx == 29 && rsi == m1 + 101 && rdi == m3 + 101);
+
+ rsi = m1; rdi = m3; rcx = 65;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsw"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsw (2)",
+ rcx == 14 && rsi == m1 + 102 && rdi == m3 + 102);
+
+ rsi = m1; rdi = m3; rcx = 32;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsl"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpll (2)",
+ rcx == 6 && rsi == m1 + 104 && rdi == m3 + 104);
+
+ rsi = m1; rdi = m3; rcx = 16;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsq"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsq (2)",
+ rcx == 3 && rsi == m1 + 104 && rdi == m3 + 104);
+
+}
+
+void test_cr8(void)
+{
+ unsigned long src, dst;
+
+ dst = 777;
+ src = 3;
+ asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]"
+ : [dst]"+r"(dst), [src]"+r"(src));
+ report("mov %cr8", dst == 3 && src == 3);
+}
+
+void test_push(void *mem)
+{
+ unsigned long tmp;
+ unsigned long *stack_top = mem + 4096;
+ unsigned long *new_stack_top;
+ unsigned long memw = 0x123456789abcdeful;
+
+ memset(mem, 0x55, (void *)stack_top - mem);
+
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "pushq $-7 \n\t"
+ "pushq %[reg] \n\t"
+ "pushq (%[mem]) \n\t"
+ "pushq $-7070707 \n\t"
+ "mov %%rsp, %[new_stack_top] \n\t"
+ "mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top)
+ : [stack_top]"r"(stack_top),
+ [reg]"r"(-17l), [mem]"r"(&memw)
+ : "memory");
+
+ report("push $imm8", stack_top[-1] == -7ul);
+ report("push %reg", stack_top[-2] == -17ul);
+ report("push mem", stack_top[-3] == 0x123456789abcdeful);
+ report("push $imm", stack_top[-4] == -7070707);
+}
+
+void test_pop(void *mem)
+{
+ unsigned long tmp;
+ unsigned long *stack_top = mem + 4096;
+ unsigned long *new_stack_top;
+ unsigned long memw = 0x123456789abcdeful;
+ static unsigned long tmp2;
+
+ memset(mem, 0x55, (void *)stack_top - mem);
+
+ asm volatile("pushq %[val] \n\t"
+ "popq (%[mem])"
+ : : [val]"m"(memw), [mem]"r"(mem) : "memory");
+ report("pop mem", *(unsigned long *)mem == memw);
+
+ memw = 7 - memw;
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "pushq %[val] \n\t"
+ "popq %[tmp2] \n\t"
+ "mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2)
+ : [val]"r"(memw), [stack_top]"r"(stack_top)
+ : "memory");
+ report("pop mem (2)", tmp2 == memw);
+
+ memw = 129443 - memw;
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "pushq %[val] \n\t"
+ "popq %[tmp2] \n\t"
+ "mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2)
+ : [val]"r"(memw), [stack_top]"r"(stack_top)
+ : "memory");
+ report("pop reg", tmp2 == memw);
+
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "push $1f \n\t"
+ "ret \n\t"
+ "2: jmp 2b \n\t"
+ "1: mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top)
+ : "memory");
+ report("ret", 1);
+}
+
+unsigned long read_cr0(void)
+{
+ unsigned long cr0;
+
+ asm volatile ("mov %%cr0, %0" : "=r"(cr0));
+ return cr0;
+}
+
+void test_smsw(void)
+{
+ char mem[16];
+ unsigned short msw, msw_orig, *pmsw;
+ int i, zero;
+
+ msw_orig = read_cr0();
+
+ asm("smsw %0" : "=r"(msw));
+ report("smsw (1)", msw == msw_orig);
+
+ memset(mem, 0, 16);
+ pmsw = (void *)mem;
+ asm("smsw %0" : "=m"(pmsw[4]));
+ zero = 1;
+ for (i = 0; i < 8; ++i)
+ if (i != 4 && pmsw[i])
+ zero = 0;
+ report("smsw (2)", msw == pmsw[4] && zero);
+}
+
+void test_lmsw(void)
+{
+ char mem[16];
+ unsigned short msw, *pmsw;
+ unsigned long cr0;
+
+ cr0 = read_cr0();
+
+ msw = cr0 ^ 8;
+ asm("lmsw %0" : : "r"(msw));
+ printf("before %lx after %lx\n", cr0, read_cr0());
+ report("lmsw (1)", (cr0 ^ read_cr0()) == 8);
+
+ pmsw = (void *)mem;
+ *pmsw = cr0;
+ asm("lmsw %0" : : "m"(*pmsw));
+ printf("before %lx after %lx\n", cr0, read_cr0());
+ report("lmsw (2)", cr0 == read_cr0());
+}
+
+int main()
+{
+ void *mem;
+ unsigned long t1, t2;
+
+ setup_vm();
+ mem = vmap(IORAM_BASE_PHYS, IORAM_LEN);
+
+ // test mov reg, r/m and mov r/m, reg
+ t1 = 0x123456789abcdef;
+ asm volatile("mov %[t1], (%[mem]) \n\t"
+ "mov (%[mem]), %[t2]"
+ : [t2]"=r"(t2)
+ : [t1]"r"(t1), [mem]"r"(mem)
+ : "memory");
+ report("mov reg, r/m (1)", t2 == 0x123456789abcdef);
+
+ test_cmps(mem);
+
+ test_push(mem);
+ test_pop(mem);
+
+ test_cr8();
+
+ test_smsw();
+ test_lmsw();
+
+ printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
+ return fails ? 1 : 0;
+}
diff --git a/kvm/user/test/x86/exit.c b/kvm/user/test/x86/exit.c
new file mode 100644
index 000000000..8903621b1
--- /dev/null
+++ b/kvm/user/test/x86/exit.c
@@ -0,0 +1,7 @@
+#include "runtime.h"
+
+void exit(unsigned code)
+{
+ asm volatile("out %al, %dx" : : "a"(code), "d"(0xf4));
+ asm volatile("cli; hlt");
+}
diff --git a/kvm/user/test/x86/hypercall.c b/kvm/user/test/x86/hypercall.c
new file mode 100644
index 000000000..95120a23b
--- /dev/null
+++ b/kvm/user/test/x86/hypercall.c
@@ -0,0 +1,31 @@
+#include "libcflat.h"
+
+#define KVM_HYPERCALL_INTEL ".byte 0x0f,0x01,0xc1"
+#define KVM_HYPERCALL_AMD ".byte 0x0f,0x01,0xd9"
+
+static inline long kvm_hypercall0_intel(unsigned int nr)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL_INTEL
+ : "=a"(ret)
+ : "a"(nr));
+ return ret;
+}
+
+static inline long kvm_hypercall0_amd(unsigned int nr)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL_AMD
+ : "=a"(ret)
+ : "a"(nr));
+ return ret;
+}
+
+int main(int ac, char **av)
+{
+ kvm_hypercall0_intel(-1u);
+ printf("Hypercall via VMCALL: OK\n");
+ kvm_hypercall0_amd(-1u);
+ printf("Hypercall via VMMCALL: OK\n");
+ return 0;
+}
diff --git a/kvm/user/test/x86/ioram.h b/kvm/user/test/x86/ioram.h
new file mode 100644
index 000000000..2938142b3
--- /dev/null
+++ b/kvm/user/test/x86/ioram.h
@@ -0,0 +1,7 @@
+#ifndef __IO_RAM_H
+#define __IO_RAM_H
+
+#define IORAM_BASE_PHYS 0xff000000UL
+#define IORAM_LEN 0x10000UL
+
+#endif
diff --git a/kvm/user/test/x86/irq.S b/kvm/user/test/x86/irq.S
new file mode 100644
index 000000000..0425db6f8
--- /dev/null
+++ b/kvm/user/test/x86/irq.S
@@ -0,0 +1,118 @@
+// irq test program. assumes outb $irq, $0xff generates an interrupt $irq.
+
+#include "print.h"
+
+.text
+ PRINT "irq test"
+ mov $stack_top, %rsp
+
+ call setup_gdt
+
+ mov %ds, %ax
+ mov %ax, %ds // check ds descriptor is okay
+
+ mov $irq_handler, %rdx
+ mov $0x20, %eax
+ call setup_idt_entry
+
+ lidt idt_descriptor
+
+ PRINT "software interrupt"
+ int $0x20
+
+ sti
+ nop
+
+ PRINT "injecting interrupt with interrupts enabled"
+
+ mov $0x20, %al
+ outb %al, $0xff // inject interrupt
+
+ nop
+ nop
+ nop
+ PRINT "after injection"
+
+ cli
+
+ PRINT "injecting interrupt with interrupts disabled"
+
+ mov $0x20, %al
+ outb %al, $0xff // inject interrupt
+
+ // no interrupt here (disabled)
+ nop
+ nop
+ PRINT "enabling interrupts"
+ nop
+ nop
+ sti
+ out %al, $0x80 // blocked by sti
+ // interrupt here
+ out %al, $0x80
+
+ PRINT "after injection"
+ nop
+ nop
+
+ hlt
+
+irq_handler:
+ PRINT "interrupt handler"
+ iretq
+
+setup_idt_entry: // %rax: irq %rdx: handler
+ shl $4, %rax
+ mov %dx, idt(%rax)
+ shr $16, %rdx
+ mov %cs, 2+idt(%rax)
+ mov %dx, 6+idt(%rax)
+ shr $16, %rdx
+ mov %edx, 8+idt(%rax)
+ movw $0x8e00, 4+idt(%rax)
+ ret
+
+setup_gdt:
+ mov $0, %eax
+ mov %cs, %ax
+ andl $~7, %eax
+ movl $0xffff, gdt(%rax)
+ movl $0xaf9b00, 4+gdt(%rax)
+
+ mov $0, %eax
+ mov %ds, %ax
+ andl $~7, %eax
+ movl $0xffff, gdt(%rax)
+ movl $0x8f9300, 4+gdt(%rax)
+
+ lgdt gdt_descriptor
+ ret
+
+.data
+
+.align 16
+
+idt:
+ . = . + 256 * 16
+
+idt_descriptor:
+ .word . - idt - 1
+ .quad idt
+
+.align 8
+
+gdt:
+ . = . + 256 * 8
+
+gdt_descriptor:
+ .word . - gdt - 1
+ .quad gdt
+
+
+.align 4096
+stack_base:
+ . = . + 4096
+stack_top:
+
+
+
diff --git a/kvm/user/test/x86/memtest1.S b/kvm/user/test/x86/memtest1.S
new file mode 100644
index 000000000..3821e867c
--- /dev/null
+++ b/kvm/user/test/x86/memtest1.S
@@ -0,0 +1,44 @@
+.text
+
+start:
+ mov $0x1000,%r8
+ mov $0x0a,%ecx
+
+init_page:
+ dec %ecx
+ jne no_io
+ mov $0x0,%al
+ out %al,$0x80
+ mov $0x0a,%ecx
+
+no_io:
+ mov %r8,(%r8)
+ add $0x1000,%r8
+ cmp $0x8000000,%r8
+ jne init_page
+ mov $0x1000,%r8
+ mov $0x0a,%ecx
+
+test_loop:
+ dec %ecx
+ jne no_io2
+ mov $0x0,%al
+ out %al,$0x80
+ mov $0x0a,%ecx
+
+no_io2:
+ mov (%r8),%r9
+ cmp %r8,%r9
+ jne err
+ add $0x1000,%r8
+ cmp $0x8000000,%r8
+ jne test_loop
+ mov $0x1000,%r8
+ jmp test_loop
+
+err:
+ mov $0xffffffffffffffff,%r12
+ mov $0xffffffffffffffff,%r13
+ mov $0x0,%al
+ out %al,$0x80
+ jmp err
diff --git a/kvm/user/test/x86/port80.c b/kvm/user/test/x86/port80.c
new file mode 100644
index 000000000..522c1a4dc
--- /dev/null
+++ b/kvm/user/test/x86/port80.c
@@ -0,0 +1,12 @@
+#include "libcflat.h"
+
+int main()
+{
+ int i;
+
+ printf("begining port 0x80 write test\n");
+ for (i = 0; i < 10000000; ++i)
+ asm volatile("outb %al, $0x80");
+ printf("done\n");
+ return 0;
+}
diff --git a/kvm/user/test/x86/print.S b/kvm/user/test/x86/print.S
new file mode 100644
index 000000000..c1b1c0d7c
--- /dev/null
+++ b/kvm/user/test/x86/print.S
@@ -0,0 +1,31 @@
+
+#include "print.h"
+
+#define PSEUDO_SERIAL_PORT 0xf1
+
+
+.text
+ PRINT "boo"
+ hlt
+1: jmp 1b
+
+.globl print
+print:
+ push %rax
+ push %rsi
+ push %rdx
+
+ mov %rdi, %rsi
+ mov $(PSEUDO_SERIAL_PORT), %edx
+
+putchar:
+ cmpb $0, (%rsi)
+ jz done
+ outsb
+ jmp putchar
+done:
+
+ pop %rdx
+ pop %rsi
+ pop %rax
+ ret
diff --git a/kvm/user/test/x86/print.h b/kvm/user/test/x86/print.h
new file mode 100644
index 000000000..d5bd2f997
--- /dev/null
+++ b/kvm/user/test/x86/print.h
@@ -0,0 +1,19 @@
+#ifndef PRINT_H
+#define PRINT_H
+
+.macro PRINT text
+
+.data
+
+333: .asciz "\text\n"
+
+.previous
+
+ push %rdi
+ lea 333b, %rdi
+ call print
+ pop %rdi
+
+.endm
+
+#endif
diff --git a/kvm/user/test/x86/realmode.c b/kvm/user/test/x86/realmode.c
new file mode 100644
index 000000000..f6d532658
--- /dev/null
+++ b/kvm/user/test/x86/realmode.c
@@ -0,0 +1,415 @@
+asm(".code16gcc");
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+typedef unsigned long long u64;
+
+void test_function(void);
+
+asm(
+ "test_function: \n\t"
+ "mov $0x1234, %eax \n\t"
+ "ret"
+ );
+
+static int strlen(const char *str)
+{
+ int n;
+
+ for (n = 0; *str; ++str)
+ ++n;
+ return n;
+}
+
+static void print_serial(const char *buf)
+{
+ unsigned long len = strlen(buf);
+
+ asm volatile ("addr32/rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+}
+
+static void exit(int code)
+{
+ asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4));
+}
+
+struct regs {
+ u32 eax, ebx, ecx, edx;
+ u32 esi, edi, esp, ebp;
+ u32 eip, eflags;
+};
+
+static u64 gdt[] = {
+ 0,
+ 0x00cf9b000000ffffull, // flat 32-bit code segment
+ 0x00cf93000000ffffull, // flat 32-bit data segment
+};
+
+static struct {
+ u16 limit;
+ void *base;
+} __attribute__((packed)) gdt_descr = {
+ sizeof(gdt) - 1,
+ gdt,
+};
+
+static void exec_in_big_real_mode(const struct regs *inregs,
+ struct regs *outregs,
+ const u8 *insn, int insn_len)
+{
+ unsigned long tmp;
+ static struct regs save;
+ int i;
+ extern u8 test_insn[], test_insn_end[];
+
+ for (i = 0; i < insn_len; ++i)
+ test_insn[i] = insn[i];
+ for (; i < test_insn_end - test_insn; ++i)
+ test_insn[i] = 0x90; // nop
+
+ save = *inregs;
+ asm volatile(
+ "lgdtl %[gdt_descr] \n\t"
+ "mov %%cr0, %[tmp] \n\t"
+ "or $1, %[tmp] \n\t"
+ "mov %[tmp], %%cr0 \n\t"
+ "mov %[bigseg], %%gs \n\t"
+ "and $-2, %[tmp] \n\t"
+ "mov %[tmp], %%cr0 \n\t"
+
+ "xchg %%eax, %[save]+0 \n\t"
+ "xchg %%ebx, %[save]+4 \n\t"
+ "xchg %%ecx, %[save]+8 \n\t"
+ "xchg %%edx, %[save]+12 \n\t"
+ "xchg %%esi, %[save]+16 \n\t"
+ "xchg %%edi, %[save]+20 \n\t"
+ "xchg %%esp, %[save]+24 \n\t"
+ "xchg %%ebp, %[save]+28 \n\t"
+
+ "test_insn: . = . + 16\n\t"
+ "test_insn_end: \n\t"
+
+ "xchg %%eax, %[save]+0 \n\t"
+ "xchg %%ebx, %[save]+4 \n\t"
+ "xchg %%ecx, %[save]+8 \n\t"
+ "xchg %%edx, %[save]+12 \n\t"
+ "xchg %%esi, %[save]+16 \n\t"
+ "xchg %%edi, %[save]+20 \n\t"
+ "xchg %%esp, %[save]+24 \n\t"
+ "xchg %%ebp, %[save]+28 \n\t"
+
+ /* Save EFLAGS in outregs*/
+ "pushfl \n\t"
+ "popl %[save]+36 \n\t"
+
+ "xor %[tmp], %[tmp] \n\t"
+ "mov %[tmp], %%gs \n\t"
+ : [tmp]"=&r"(tmp), [save]"+m"(save)
+ : [gdt_descr]"m"(gdt_descr), [bigseg]"r"((short)16)
+ : "cc", "memory"
+ );
+ *outregs = save;
+}
+
+#define R_AX 1
+#define R_BX 2
+#define R_CX 4
+#define R_DX 8
+#define R_SI 16
+#define R_DI 32
+#define R_SP 64
+#define R_BP 128
+
+int regs_equal(const struct regs *r1, const struct regs *r2, int ignore)
+{
+ const u32 *p1 = &r1->eax, *p2 = &r2->eax; // yuck
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ if (!(ignore & (1 << i)) && p1[i] != p2[i])
+ return 0;
+ return 1;
+}
+
+#define MK_INSN(name, str) \
+ asm ( \
+ ".pushsection \".text\" \n\t" \
+ "insn_" #name ": " str " \n\t" \
+ "insn_" #name "_end: \n\t" \
+ ".popsection \n\t" \
+ ); \
+ extern u8 insn_##name[], insn_##name##_end[]
+
+void test_shld(void)
+{
+ struct regs inregs = { .eax = 0xbe, .edx = 0xef000000 }, outregs;
+ MK_INSN(shld_test, "shld $8,%edx,%eax\n\t");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_shld_test,
+ insn_shld_test_end - insn_shld_test);
+ if (outregs.eax != 0xbeef)
+ print_serial("shld: failure\n");
+ else
+ print_serial("shld: success\n");
+}
+
+void test_mov_imm(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ MK_INSN(mov_r32_imm_1, "mov $1234567890, %eax");
+ MK_INSN(mov_r16_imm_1, "mov $1234, %ax");
+ MK_INSN(mov_r8_imm_1, "mov $0x12, %ah");
+ MK_INSN(mov_r8_imm_2, "mov $0x34, %al");
+ MK_INSN(mov_r8_imm_3, "mov $0x12, %ah\n\t" "mov $0x34, %al\n\t");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_mov_r16_imm_1,
+ insn_mov_r16_imm_1_end - insn_mov_r16_imm_1);
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 1234)
+ print_serial("mov test 1: FAIL\n");
+
+ /* test mov $imm, %eax */
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_mov_r32_imm_1,
+ insn_mov_r32_imm_1_end - insn_mov_r32_imm_1);
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 1234567890)
+ print_serial("mov test 2: FAIL\n");
+
+ /* test mov $imm, %al/%ah */
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_mov_r8_imm_1,
+ insn_mov_r8_imm_1_end - insn_mov_r8_imm_1);
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1200)
+ print_serial("mov test 3: FAIL\n");
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_mov_r8_imm_2,
+ insn_mov_r8_imm_2_end - insn_mov_r8_imm_2);
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x34)
+ print_serial("mov test 4: FAIL\n");
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_mov_r8_imm_3,
+ insn_mov_r8_imm_3_end - insn_mov_r8_imm_3);
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234)
+ print_serial("mov test 5: FAIL\n");
+}
+
+void test_cmp_imm(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ MK_INSN(cmp_test1, "mov $0x34, %al\n\t"
+ "cmp $0x34, %al\n\t");
+ MK_INSN(cmp_test2, "mov $0x34, %al\n\t"
+ "cmp $0x39, %al\n\t");
+ MK_INSN(cmp_test3, "mov $0x34, %al\n\t"
+ "cmp $0x24, %al\n\t");
+
+ /* test cmp imm8 with AL */
+ /* ZF: (bit 6) Zero Flag becomes 1 if an operation results
+ * in a 0 writeback, or 0 register
+ */
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_cmp_test1,
+ insn_cmp_test1_end - insn_cmp_test1);
+ if ((outregs.eflags & (1<<6)) != (1<<6))
+ print_serial("cmp test 1: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_cmp_test2,
+ insn_cmp_test2_end - insn_cmp_test2);
+ if ((outregs.eflags & (1<<6)) != 0)
+ print_serial("cmp test 2: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_cmp_test3,
+ insn_cmp_test3_end - insn_cmp_test3);
+ if ((outregs.eflags & (1<<6)) != 0)
+ print_serial("cmp test 3: FAIL\n");
+}
+
+void test_add_imm(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ MK_INSN(add_test1, "mov $0x43211234, %eax \n\t"
+ "add $0x12344321, %eax \n\t");
+ MK_INSN(add_test2, "mov $0x12, %eax \n\t"
+ "add $0x21, %al\n\t");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_add_test1,
+ insn_add_test1_end - insn_add_test1);
+ if (outregs.eax != 0x55555555)
+ print_serial("add test 1: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_add_test2,
+ insn_add_test2_end - insn_add_test2);
+ if (outregs.eax != 0x33)
+ print_serial("add test 2: FAIL\n");
+}
+
+void test_eflags_insn(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ MK_INSN(clc, "clc");
+ MK_INSN(cli, "cli");
+ MK_INSN(sti, "sti");
+ MK_INSN(cld, "cld");
+ MK_INSN(std, "std");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_clc,
+ insn_clc_end - insn_clc);
+ if (outregs.eflags & 1)
+ print_serial("clc test: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_cli,
+ insn_cli_end - insn_cli);
+ if (outregs.eflags & (1 << 9))
+ print_serial("cli test: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_sti,
+ insn_sti_end - insn_sti);
+ if (!(outregs.eflags & (1 << 9)))
+ print_serial("sti test: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_cld,
+ insn_cld_end - insn_cld);
+ if (outregs.eflags & (1 << 10))
+ print_serial("cld test: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_std,
+ insn_std_end - insn_std);
+ if (!(outregs.eflags & (1 << 10)))
+ print_serial("std test: FAIL\n");
+}
+
+void test_io(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ MK_INSN(io_test1, "mov $0xff, %al \n\t"
+ "out %al, $0x10 \n\t"
+ "in $0x10, %al \n\t");
+ MK_INSN(io_test2, "mov $0xffff, %ax \n\t"
+ "out %ax, $0x10 \n\t"
+ "in $0x10, %ax \n\t");
+ MK_INSN(io_test3, "mov $0xffffffff, %eax \n\t"
+ "out %eax, $0x10 \n\t"
+ "in $0x10, %eax \n\t");
+ MK_INSN(io_test4, "mov $0x10, %dx \n\t"
+ "mov $0xff, %al \n\t"
+ "out %al, %dx \n\t"
+ "in %dx, %al \n\t");
+ MK_INSN(io_test5, "mov $0x10, %dx \n\t"
+ "mov $0xffff, %ax \n\t"
+ "out %ax, %dx \n\t"
+ "in %dx, %ax \n\t");
+ MK_INSN(io_test6, "mov $0x10, %dx \n\t"
+ "mov $0xffffffff, %eax \n\t"
+ "out %eax, %dx \n\t"
+ "in %dx, %eax \n\t");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_io_test1,
+ insn_io_test1_end - insn_io_test1);
+
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xff)
+ print_serial("I/O test 1: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_io_test2,
+ insn_io_test2_end - insn_io_test2);
+
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xffff)
+ print_serial("I/O test 2: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_io_test3,
+ insn_io_test3_end - insn_io_test3);
+
+ if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xffffffff)
+ print_serial("I/O test 3: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_io_test4,
+ insn_io_test4_end - insn_io_test4);
+
+ if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xff)
+ print_serial("I/O test 4: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_io_test5,
+ insn_io_test5_end - insn_io_test5);
+
+ if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xffff)
+ print_serial("I/O test 5: FAIL\n");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_io_test6,
+ insn_io_test6_end - insn_io_test6);
+
+ if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xffffffff)
+ print_serial("I/O test 6: FAIL\n");
+
+}
+
+void test_call(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ MK_INSN(call1, "mov $test_function, %eax \n\t"
+ "call *%eax\n\t");
+
+ exec_in_big_real_mode(&inregs, &outregs,
+ insn_call1,
+ insn_call1_end - insn_call1);
+ if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234)
+ print_serial("Call Test 1: FAIL\n");
+}
+
+void test_null(void)
+{
+ struct regs inregs = { 0 }, outregs;
+ exec_in_big_real_mode(&inregs, &outregs, 0, 0);
+ if (!regs_equal(&inregs, &outregs, 0))
+ print_serial("null test: FAIL\n");
+}
+
+void start(void)
+{
+ test_null();
+
+ test_shld();
+ test_mov_imm();
+ test_cmp_imm();
+ test_add_imm();
+ test_io();
+ test_eflags_insn();
+
+ exit(0);
+}
+
+asm(
+ ".data \n\t"
+ ". = . + 4096 \n\t"
+ "stacktop: \n\t"
+ ".text \n\t"
+ "init: \n\t"
+ "xor %ax, %ax \n\t"
+ "mov %ax, %ds \n\t"
+ "mov %ax, %es \n\t"
+ "mov %ax, %ss \n\t"
+ "mov $0x4000, %cx \n\t"
+ "xor %esi, %esi \n\t"
+ "mov %esi, %edi \n\t"
+ "rep/addr32/cs/movsl \n\t"
+ "mov $stacktop, %sp\n\t"
+ "ljmp $0, $start \n\t"
+ ".pushsection .boot, \"ax\" \n\t"
+ "ljmp $0xf000, $init \n\t"
+ ".popsection"
+ );
diff --git a/kvm/user/test/x86/realmode.lds b/kvm/user/test/x86/realmode.lds
new file mode 100644
index 000000000..c9cdd7d55
--- /dev/null
+++ b/kvm/user/test/x86/realmode.lds
@@ -0,0 +1,16 @@
+OUTPUT_FORMAT(binary)
+
+SECTIONS
+{
+ . = 0;
+ stext = .;
+ .text : { *(.init) *(.text) }
+ . = ALIGN(4K);
+ .data : { *(.data) *(.rodata*) }
+ . = ALIGN(16);
+ .bss : { *(.bss) }
+ . = 0xfff0;
+ .boot : { *(.boot) }
+ edata = .;
+}
+
diff --git a/kvm/user/test/x86/runtime.h b/kvm/user/test/x86/runtime.h
new file mode 100644
index 000000000..4b4c30292
--- /dev/null
+++ b/kvm/user/test/x86/runtime.h
@@ -0,0 +1,6 @@
+#ifndef H_RUNTIME
+#define H_RUNTIME
+
+void exit(unsigned code) __attribute__((__noreturn__));
+
+#endif
diff --git a/kvm/user/test/x86/sieve.c b/kvm/user/test/x86/sieve.c
new file mode 100644
index 000000000..a707b92dc
--- /dev/null
+++ b/kvm/user/test/x86/sieve.c
@@ -0,0 +1,89 @@
+#include "vm.h"
+
+void print(const char *text);
+
+void printi(int n)
+{
+ char buf[10], *p = buf;
+ int s = 0, i;
+
+ if (n < 0) {
+ n = -n;
+ s = 1;
+ }
+
+ while (n) {
+ *p++ = '0' + n % 10;
+ n /= 10;
+ }
+
+ if (s)
+ *p++ = '-';
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print(buf);
+}
+
+int sieve(char* data, int size)
+{
+ int i, j, r = 0;
+
+ for (i = 0; i < size; ++i)
+ data[i] = 1;
+
+ data[0] = data[1] = 0;
+
+ for (i = 2; i < size; ++i)
+ if (data[i]) {
+ ++r;
+ for (j = i*2; j < size; j += i)
+ data[j] = 0;
+ }
+ return r;
+}
+
+void test_sieve(const char *msg, char *data, int size)
+{
+ int r;
+
+ print(msg);
+ print(": ");
+ r = sieve(data, size);
+ printi(r);
+ print("\n");
+}
+
+#define STATIC_SIZE 1000000
+#define VSIZE 100000000
+char static_data[STATIC_SIZE];
+
+int main()
+{
+ void *v;
+ int i;
+
+ print("starting sieve\n");
+ test_sieve("static", static_data, STATIC_SIZE);
+ setup_vm();
+ print("mapped: ");
+ test_sieve("mapped", static_data, STATIC_SIZE);
+ for (i = 0; i < 30; ++i) {
+ v = vmalloc(VSIZE);
+ test_sieve("virtual", v, VSIZE);
+ vfree(v);
+ }
+
+ return 0;
+}
diff --git a/kvm/user/test/x86/simple.S b/kvm/user/test/x86/simple.S
new file mode 100644
index 000000000..f3c844fc7
--- /dev/null
+++ b/kvm/user/test/x86/simple.S
@@ -0,0 +1,13 @@
+
+ .text
+
+ mov $0, %al
+ mov $10000, %ebx
+1:
+ mov %rbx, %rcx
+2:
+ loop 2b
+ out %al, $0x80
+ inc %al
+ add $10000, %rbx
+ jmp 1b
diff --git a/kvm/user/test/x86/smptest.c b/kvm/user/test/x86/smptest.c
new file mode 100644
index 000000000..7b1ba498b
--- /dev/null
+++ b/kvm/user/test/x86/smptest.c
@@ -0,0 +1,31 @@
+#include "libcflat.h"
+#include "smp.h"
+
+static void ipi_test(void *data)
+{
+ int n = (long)data;
+
+ printf("ipi called, cpu %d\n", n);
+ if (n != smp_id())
+ printf("but wrong cpu %d\n", smp_id());
+}
+
+static void smp_main(void)
+{
+ printf("smp main %d\n", smp_id());
+ while (1)
+ asm volatile ("hlt" : : : "memory");
+}
+
+int main()
+{
+ int ncpus;
+ int i;
+
+ smp_init(smp_main);
+ ncpus = cpu_count();
+ printf("found %d cpus\n", ncpus);
+ for (i = 0; i < ncpus; ++i)
+ on_cpu(i, ipi_test, (void *)(long)i);
+ return 0;
+}
diff --git a/kvm/user/test/x86/stringio.S b/kvm/user/test/x86/stringio.S
new file mode 100644
index 000000000..31ddc479f
--- /dev/null
+++ b/kvm/user/test/x86/stringio.S
@@ -0,0 +1,31 @@
+
+.data
+
+.macro str name, value
+
+\name : .long 1f-2f
+2: .ascii "\value"
+1:
+.endm
+
+ str "forward", "forward"
+ str "backward", "backward"
+
+.text
+
+
+ cld
+ movl forward, %ecx
+ lea 4+forward, %rsi
+ movw $1, %dx
+ rep outsb
+
+ std
+ movl backward, %ecx
+ lea 4+backward-1(%rcx), %rsi
+ movw $2, %dx
+ rep outsb
+
+ hlt
+
+
diff --git a/kvm/user/test/x86/test32.S b/kvm/user/test/x86/test32.S
new file mode 100644
index 000000000..a2e0fd7a2
--- /dev/null
+++ b/kvm/user/test/x86/test32.S
@@ -0,0 +1,8 @@
+.code32
+
+.text
+
+1:
+ mov $0x12, %al
+ out %al, $0x80
+ jmp 1b
diff --git a/kvm/user/test/x86/tsc.c b/kvm/user/test/x86/tsc.c
new file mode 100644
index 000000000..204b1fd99
--- /dev/null
+++ b/kvm/user/test/x86/tsc.c
@@ -0,0 +1,40 @@
+#include "libcflat.h"
+
+typedef unsigned long long u64;
+
+u64 rdtsc(void)
+{
+ unsigned a, d;
+
+ asm volatile("rdtsc" : "=a"(a), "=d"(d));
+ return a | (u64)d << 32;
+}
+
+void wrtsc(u64 tsc)
+{
+ unsigned a = tsc, d = tsc >> 32;
+
+ asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(0x10));
+}
+
+void test_wrtsc(u64 t1)
+{
+ u64 t2;
+
+ wrtsc(t1);
+ t2 = rdtsc();
+ printf("rdtsc after wrtsc(%lld): %lld\n", t1, t2);
+}
+
+int main()
+{
+ u64 t1, t2;
+
+ t1 = rdtsc();
+ t2 = rdtsc();
+ printf("rdtsc latency %lld\n", (unsigned)(t2 - t1));
+
+ test_wrtsc(0);
+ test_wrtsc(100000000000ull);
+ return 0;
+}
diff --git a/kvm/user/test/x86/vm.c b/kvm/user/test/x86/vm.c
new file mode 100644
index 000000000..03c73546b
--- /dev/null
+++ b/kvm/user/test/x86/vm.c
@@ -0,0 +1,268 @@
+
+#include "vm.h"
+
+void print(const char *s);
+
+#define PAGE_SIZE 4096ul
+#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
+
+static void *free = 0;
+static void *vfree_top = 0;
+
+static unsigned long virt_to_phys(const void *virt)
+{
+ return (unsigned long)virt;
+}
+
+static void *phys_to_virt(unsigned long phys)
+{
+ return (void *)phys;
+}
+
+void *memset(void *data, int c, unsigned long len)
+{
+ char *s = data;
+
+ while (len--)
+ *s++ = c;
+
+ return data;
+}
+
+static void free_memory(void *mem, unsigned long size)
+{
+ while (size >= PAGE_SIZE) {
+ *(void **)mem = free;
+ free = mem;
+ mem += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+void *alloc_page()
+{
+ void *p;
+
+ if (!free)
+ return 0;
+
+ p = free;
+ free = *(void **)free;
+
+ return p;
+}
+
+void free_page(void *page)
+{
+ *(void **)page = free;
+ free = page;
+}
+
+extern char edata;
+static unsigned long end_of_memory;
+
+#define PTE_PRESENT (1ull << 0)
+#define PTE_PSE (1ull << 7)
+#define PTE_WRITE (1ull << 1)
+#define PTE_ADDR (0xffffffffff000ull)
+
+static void install_pte(unsigned long *cr3,
+ int pte_level,
+ void *virt,
+ unsigned long pte)
+{
+ int level;
+ unsigned long *pt = cr3;
+ unsigned offset;
+
+ for (level = 4; level > pte_level; --level) {
+ offset = ((unsigned long)virt >> ((level-1) * 9 + 12)) & 511;
+ if (!(pt[offset] & PTE_PRESENT)) {
+ unsigned long *new_pt = alloc_page();
+ memset(new_pt, 0, PAGE_SIZE);
+ pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE;
+ }
+ pt = phys_to_virt(pt[offset] & 0xffffffffff000ull);
+ }
+ offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
+ pt[offset] = pte;
+}
+
+static unsigned long get_pte(unsigned long *cr3, void *virt)
+{
+ int level;
+ unsigned long *pt = cr3, pte;
+ unsigned offset;
+
+ for (level = 4; level > 1; --level) {
+ offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
+ pte = pt[offset];
+ if (!(pte & PTE_PRESENT))
+ return 0;
+ if (level == 2 && (pte & PTE_PSE))
+ return pte;
+ pt = phys_to_virt(pte & 0xffffffffff000ull);
+ }
+ offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
+ pte = pt[offset];
+ return pte;
+}
+
+static void install_large_page(unsigned long *cr3,
+ unsigned long phys,
+ void *virt)
+{
+ install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE);
+}
+
+static void install_page(unsigned long *cr3,
+ unsigned long phys,
+ void *virt)
+{
+ install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE);
+}
+
+static inline void load_cr3(unsigned long cr3)
+{
+ asm ( "mov %0, %%cr3" : : "r"(cr3) );
+}
+
+static inline unsigned long read_cr3()
+{
+ unsigned long cr3;
+
+ asm volatile ( "mov %%cr3, %0" : "=r"(cr3) );
+ return cr3;
+}
+
+static inline void load_cr0(unsigned long cr0)
+{
+ asm volatile ( "mov %0, %%cr0" : : "r"(cr0) );
+}
+
+static inline unsigned long read_cr0()
+{
+ unsigned long cr0;
+
+ asm volatile ( "mov %%cr0, %0" : "=r"(cr0) );
+ return cr0;
+}
+
+static inline void load_cr4(unsigned long cr4)
+{
+ asm volatile ( "mov %0, %%cr4" : : "r"(cr4) );
+}
+
+static inline unsigned long read_cr4()
+{
+ unsigned long cr4;
+
+ asm volatile ( "mov %%cr4, %0" : "=r"(cr4) );
+ return cr4;
+}
+
+struct gdt_table_descr
+{
+ unsigned short len;
+ unsigned long *table;
+} __attribute__((packed));
+
+static inline void load_gdt(unsigned long *table, int nent)
+{
+ struct gdt_table_descr descr;
+
+ descr.len = nent * 8 - 1;
+ descr.table = table;
+ asm volatile ( "lgdt %0" : : "m"(descr) );
+}
+
+#define SEG_CS_32 8
+#define SEG_CS_64 16
+
+struct ljmp {
+ void *ofs;
+ unsigned short seg;
+};
+
+static void setup_mmu(unsigned long len)
+{
+ unsigned long *cr3 = alloc_page();
+ unsigned long phys = 0;
+
+ memset(cr3, 0, PAGE_SIZE);
+ while (phys + LARGE_PAGE_SIZE <= len) {
+ install_large_page(cr3, phys, (void *)phys);
+ phys += LARGE_PAGE_SIZE;
+ }
+ while (phys + PAGE_SIZE <= len) {
+ install_page(cr3, phys, (void *)phys);
+ phys += PAGE_SIZE;
+ }
+
+ load_cr3(virt_to_phys(cr3));
+ print("paging enabled\n");
+}
+
+static unsigned int inl(unsigned short port)
+{
+ unsigned int val;
+ asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
+ return val;
+}
+
+void setup_vm()
+{
+ end_of_memory = inl(0xd1);
+ free_memory(&edata, end_of_memory - (unsigned long)&edata);
+ setup_mmu(end_of_memory);
+}
+
+void *vmalloc(unsigned long size)
+{
+ void *mem, *p;
+ unsigned pages;
+
+ size += sizeof(unsigned long);
+
+ size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ vfree_top -= size;
+ mem = p = vfree_top;
+ pages = size / PAGE_SIZE;
+ while (pages--) {
+ install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p);
+ p += PAGE_SIZE;
+ }
+ *(unsigned long *)mem = size;
+ mem += sizeof(unsigned long);
+ return mem;
+}
+
+void vfree(void *mem)
+{
+ unsigned long size = ((unsigned long *)mem)[-1];
+
+ while (size) {
+ free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR));
+ mem += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+void *vmap(unsigned long long phys, unsigned long size)
+{
+ void *mem, *p;
+ unsigned pages;
+
+ size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ vfree_top -= size;
+ phys &= ~(unsigned long long)(PAGE_SIZE - 1);
+
+ mem = p = vfree_top;
+ pages = size / PAGE_SIZE;
+ while (pages--) {
+ install_page(phys_to_virt(read_cr3()), phys, p);
+ phys += PAGE_SIZE;
+ p += PAGE_SIZE;
+ }
+ return mem;
+}
diff --git a/kvm/user/test/x86/vm.h b/kvm/user/test/x86/vm.h
new file mode 100644
index 000000000..0a481f133
--- /dev/null
+++ b/kvm/user/test/x86/vm.h
@@ -0,0 +1,10 @@
+#ifndef VM_H
+#define VM_H
+
+void setup_vm();
+
+void *vmalloc(unsigned long size);
+void vfree(void *mem);
+void *vmap(unsigned long long phys, unsigned long size);
+
+#endif
diff --git a/kvm/user/test/x86/vmexit.c b/kvm/user/test/x86/vmexit.c
new file mode 100644
index 000000000..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/kvm/vgabios/.cvsignore b/kvm/vgabios/.cvsignore
new file mode 100644
index 000000000..1df04b726
--- /dev/null
+++ b/kvm/vgabios/.cvsignore
@@ -0,0 +1 @@
+vbetables.h
diff --git a/kvm/vgabios/BUGS b/kvm/vgabios/BUGS
new file mode 100644
index 000000000..785f4dc37
--- /dev/null
+++ b/kvm/vgabios/BUGS
@@ -0,0 +1,3 @@
+Not all the functions have been implemented yet.
+
+Please report any bugs to <info@vruppert.de>
diff --git a/kvm/vgabios/COPYING b/kvm/vgabios/COPYING
new file mode 100644
index 000000000..223ede7de
--- /dev/null
+++ b/kvm/vgabios/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/kvm/vgabios/ChangeLog b/kvm/vgabios/ChangeLog
new file mode 100644
index 000000000..75be5bdda
--- /dev/null
+++ b/kvm/vgabios/ChangeLog
@@ -0,0 +1,1264 @@
+2008-05-11 08:40 vruppert
+
+ * biossums.c (1.6):
+
+ - fixed a warning
+
+2008-03-02 08:47 vruppert
+
+ * vbe.c (1.60):
+
+ - added debug message for unsupported VBE modes
+
+2008-02-24 09:18 vruppert
+
+ * vbe.c (1.59):
+
+ - in LFB modes the number of banks must be set to 1
+
+2008-01-27 10:44 vruppert
+
+ * Makefile (1.21), biossums.c (1.5), vgabios.c (1.67):
+
+ - added PCI data structure for the Cirrus VGABIOS images
+ - added support for the PCI data structure in biossums
+ - updated year in copyright
+
+2008-01-26 11:46 vruppert
+
+ * BUGS (1.4), Makefile (1.20), README (1.14), TODO (1.13), vbe_display_api.txt (1.14):
+
+ - whitespace cleanup
+
+2006-11-26 10:43 vruppert
+
+ * Makefile (1.19):
+
+ - disable the generation of linemarkers by the preprocessor, since the latest
+ versions of bcc don't like them
+
+2006-09-02 13:15 vruppert
+
+ * biossums.c (1.4):
+
+ - the biossums utility no longer modifies VGABIOS images with proper checksum
+ and size
+
+2006-08-19 14:28 vruppert
+
+ * Changelog (1.26), README (1.13), TODO (1.12):
+
+ - updates for 0.6a release
+
+2006-08-19 09:39 vruppert
+
+ * vbe.c (1.58):
+
+ - improved VGA compatible setup for VBE modes (disable CGA and Hercules
+ compatible memory layout)
+
+2006-08-18 20:39 vruppert
+
+ * vbe.c (1.57):
+
+ - improved VGA compatible setup for >=8bpp VBE modes (CRTC doubleword mode and
+ GRDC shift register setting added)
+ - now using symbolic name for CRTC address register
+
+2006-08-15 20:42 vruppert
+
+ * vbe.c (1.56), vbetables-gen.c (1.4):
+
+ - init 4bpp VBE modes by a temporary switch to VGA mode 0x6A
+ - all 4bpp VBE modes now enabled
+
+2006-08-14 20:24 vruppert
+
+ * vbe.c (1.55):
+
+ - VGA compatible setup for VBE modes improved (Bochs hack can be removed now)
+
+2006-08-12 07:51 vruppert
+
+ * .cvsignore (1.1):
+
+ - .cvsignore added for auto-generated file
+
+2006-08-12 07:47 vruppert
+
+ * vbe.c (1.54), vbe.h (1.27), vbe_display_api.txt (1.13), vbetables-gen.c (1.3):
+
+ - cleaned up VBE memory size definitions (removed duplicate defines, main
+ definition now in vbetables-gen.c)
+
+2006-08-09 21:28 vruppert
+
+ * vbetables.h (1.30):
+
+ - removed auto-generated file
+
+2006-08-09 21:26 vruppert
+
+ * vbe.c (1.53), vbe.h (1.26), vbe_display_api.txt (1.12), vbetables-gen.c (1.2),
+ vbetables.h (1.29):
+
+ - VBE video memory increased to 8 MB
+ - VBE dispi ID changed to B0C4
+ - documentation update
+
+2006-07-11 08:03 vruppert
+
+ * Makefile (1.18), vbetables-gen.c (1.1), vbetables.h (1.28):
+
+ - generate vbetables.h dynamicly
+ * initial patch from the qemu project by Fabrice Bellard
+ * only add modes that fit in video memory (still 4 MB)
+ * several other fixes (e.g. 4 bpp specific stuff, number of pages)
+
+2006-07-10 07:47 vruppert
+
+ * vgabios.c (1.66):
+
+ - biosfn_scroll(): check variable 'i' for underflowing when scrolling downwards
+ to avoid screen corruption
+
+2006-07-10 07:47 vruppert
+
+ * vbe.c (1.52):
+
+ - VBE set bank functions failure handling added
+ - VBE get/set logical scan line length fixes for the 4bpp mode
+
+2006-07-08 13:27 vruppert
+
+ * vbe.c (1.51), vbetables.h (1.27):
+
+ - added special case for the 4 bpp when setting VBE display start
+ - VBE mode table fixes
+
+2006-07-07 13:30 vruppert
+
+ * clext.c (1.12):
+
+ - bank pointer must be set to 0 after a mode set
+
+2006-06-21 16:58 vruppert
+
+ * vbe.c (1.50), vbetables.h (1.26):
+
+ - improved VBE display capabilities check (X resulution checked now)
+ - removed obsolete defines (LFB always available, always generate dynamic list)
+ - CR/LF to LF fixes
+
+2006-06-18 15:22 vruppert
+
+ * clext.c (1.11), vbe.c (1.49), vbe.h (1.25), vbetables.h (1.25), vgabios.c
+ (1.65):
+
+ - applied patch from the qemu project (Fabrice Bellard)
+ * Cirrus SVGA now supports the "no clear" bit when switching to Cirrus or
+ VESA mode
+ * Bochs VBE protected mode interface improved
+ * save/restore video state support for Bochs VBE and standard VGA added
+ * Bochs VBE prepared for more modi
+
+2006-03-25 10:19 vruppert
+
+ * clext.c (1.10), vgabios.c (1.64), vgatables.h (1.10):
+
+ - applied patch from Fabrice Bellard
+ * added minimal support for the video parameter table (VPT)
+ * added Cirrus SVGA mode 0x7b (1600x1200x8)
+
+2005-12-26 19:50 vruppert
+
+ * vbe.c (1.48), vgabios.c (1.63):
+
+ - Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com)
+
+2005-12-26 19:50 vruppert
+
+ * biossums.c (1.3):
+
+ - biossums utility now supports VGABIOS sizes up to 64 kBytes
+
+2005-09-21 18:45 vruppert
+
+ * vgatables.h (1.9):
+
+ - mode 0x11: all color planes must be enabled in this 2-color VGA mode
+
+2005-08-30 18:41 vruppert
+
+ * biossums.c (1.2):
+
+ - missing license text added in biossums.c
+
+2005-07-02 18:39 vruppert
+
+ * vgabios.c (1.62):
+
+ - BIOS configuration word usually reports initial mode 80x25 color text
+ - vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the
+ cursor row value
+
+2005-05-24 16:50 vruppert
+
+ * vbe.c (1.47), vgabios.c (1.61):
+
+ - output to the vgabios info port can be disabled now. It is still enabled by
+ default and always possible in debug mode. (based on a patch from Alex Beregszaszi)
+
+2005-05-20 16:06 vruppert
+
+ * vbe.c (1.46), vgabios.c (1.60):
+
+ - fixed return value for the default case in the VBE section (non-debug mode)
+ - removed unused macros HALT and PANIC_PORT
+
+2005-03-07 20:39 vruppert
+
+ * README (1.9):
+
+ - updates for 0.5a release
+
+2005-03-06 13:06 vruppert
+
+ * Makefile (1.17):
+
+ - vgabios files with cirrus support added to release target
+
+2005-03-06 12:24 vruppert
+
+ * Makefile (1.16):
+
+ - cross compilation support added (patch from Alex Beregszaszi)
+
+2005-03-05 13:03 vruppert
+
+ * BUGS (1.3), README (1.8), TODO (1.11):
+
+ - documentation updates
+
+2004-12-04 15:26 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.61), VGABIOS-lgpl-latest.cirrus.bin
+ (1.13), VGABIOS-lgpl-latest.cirrus.debug.bin (1.13),
+ VGABIOS-lgpl-latest.debug.bin (1.61), clext.c (1.9):
+
+ - Cirrus extension: support for 1280x1024x15 and 1280x1024x16 modes added (patch
+ from Fabrice Bellard)
+
+2004-08-08 16:53 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.60), VGABIOS-lgpl-latest.cirrus.bin (1.12),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.12),
+ VGABIOS-lgpl-latest.debug.bin (1.60), clext.c (1.8):
+
+ - use single bank mode for VBE
+ - enable 16k granularity for VBE only
+
+2004-07-30 19:33 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.59), VGABIOS-lgpl-latest.cirrus.bin (1.11),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.11),
+ VGABIOS-lgpl-latest.debug.bin (1.59), clext.c (1.7):
+
+ - cirrus init: set standard vga mode and reset bitblt
+
+2004-07-22 18:38 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.58), VGABIOS-lgpl-latest.cirrus.bin (1.10),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.10),
+ VGABIOS-lgpl-latest.debug.bin (1.58), clext.c (1.6), vbe.c (1.45),
+ vbetables.h (1.24):
+
+ - cirrus extension: tables for mode 1280x1024x8 added
+ - vbe: dispi_set_xres() and dispi_set_virt_width() now modify vga compatible
+ registers
+ - vbe: mode list entry for mode 800x600x4 fixed
+
+2004-07-18 20:23 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.57), VGABIOS-lgpl-latest.cirrus.bin (1.9),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.9),
+ VGABIOS-lgpl-latest.debug.bin (1.57), vgabios.c (1.59), vgatables.h (1.8):
+
+ - disable CRTC write protection before setting new values
+ - CRTC line for mode 0x6a fixed
+
+2004-07-07 16:08 vruppert
+
+ * Makefile (1.15), VGABIOS-lgpl-latest.bin (1.56),
+ VGABIOS-lgpl-latest.cirrus.bin (1.8), VGABIOS-lgpl-latest.cirrus.debug.bin (1.8),
+ VGABIOS-lgpl-latest.debug.bin (1.56), biossums.c (1.1), clext.c (1.5):
+
+ - biossums utility for the Bochs BIOS adapted for the LGPL'd VGABIOS
+ - VESA3 PMINFO checksum calculated in the source
+ - 24 bpp mode entries fixed (patch from Fabrice Bellard)
+
+2004-06-25 18:28 vruppert
+
+ * VGABIOS-lgpl-latest.cirrus.bin (1.7), VGABIOS-lgpl-latest.cirrus.debug.bin (1.7),
+ clext.c (1.4):
+
+ - 4MB memory probe added (patch from Fabrice Bellard)
+
+2004-06-25 17:31 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.55), VGABIOS-lgpl-latest.cirrus.bin (1.6),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.6),
+ VGABIOS-lgpl-latest.debug.bin (1.55), clext.c (1.3):
+
+ - fixed value of sequencer reset register in cirrus mode table
+ - fixed possible overflow error if cirrus start address is >256k
+
+2004-06-23 21:11 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.54), VGABIOS-lgpl-latest.cirrus.bin (1.5),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.5),
+ VGABIOS-lgpl-latest.debug.bin (1.54), clext.c (1.2):
+
+ - applied new patch for the cirrus extension from suzu
+ * enable VESA LFB support if a Cirrus PCI adapter is detected
+ * prepared VBE3 protected mode info block (test case required)
+ - added VBE functions 4F06h and 4F07h
+ - some bugfixes
+
+2004-06-17 18:57 vruppert
+
+ * Makefile (1.14), VGABIOS-lgpl-latest.bin (1.53),
+ VGABIOS-lgpl-latest.cirrus.bin (1.2), VGABIOS-lgpl-latest.cirrus.debug.bin (1.2),
+ VGABIOS-lgpl-latest.debug.bin (1.53):
+
+ - fixed makefile targets for the binaries with cirrus extension
+
+2004-06-16 21:11 vruppert
+
+ * Makefile (1.13), VGABIOS-lgpl-latest.bin (1.52),
+ VGABIOS-lgpl-latest.cirrus.bin (1.1), VGABIOS-lgpl-latest.cirrus.debug.bin (1.1),
+ VGABIOS-lgpl-latest.debug.bin (1.52), clext.c (1.1), vgabios.c (1.58):
+
+ - applied suzu's cirrus extension patch. Cirrus SVGA detection, most of the
+ cirrus-specific modes and some basic VBE features are present now.
+
+2004-05-31 21:15 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.51), VGABIOS-lgpl-latest.debug.bin (1.51),
+ vgabios.c (1.57):
+
+ - write character in planar graphics modes: sequencer map mask must be 0x0f and
+ bit operation must be 'replace' if bit 7 of attribute is clear
+ - read/write pixel in planar graphics modes: bit mask setup simplified
+
+2004-05-11 18:08 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.50), VGABIOS-lgpl-latest.debug.bin (1.50),
+ vgabios.c (1.56):
+
+ - biosfn_select_vert_res rewritten in assembler
+ - scroll text in planar graphics modes: attribute for blank line fixed
+ - write character in planar graphics modes: graphics controller values fixed
+
+2004-05-09 20:32 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.49), VGABIOS-lgpl-latest.debug.bin (1.49),
+ vbe.c (1.44), vbe.h (1.24), vgabios.c (1.55):
+
+ - VBE init code and some dispi ioport functions rewritten in assembler
+ - text scroll functions for CGA graphics modes added
+ - scroll text in graphics modes: attribute for blank line fixed
+
+2004-05-08 16:06 vruppert
+
+ * BUGS (1.2), README (1.7), TODO (1.10), VGABIOS-lgpl-latest.bin (1.48),
+ VGABIOS-lgpl-latest.debug.bin (1.48), vbe.c (1.43), vbe.h (1.23),
+ vbe_display_api.txt (1.11), vgabios.c (1.54):
+
+ - VBE internal functions dispi_set_enable and dispi_set_bank now called both from C
+ and asm code
+ - VBE function 0x03 rewritten in assembler
+ - VBE function 0x08 cleaned up
+ - text output and scroll functions for graphics modes rewritten using case
+ structures
+ - documentation and comments updated
+
+2004-05-06 21:18 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.47), VGABIOS-lgpl-latest.debug.bin (1.47),
+ vbe.c (1.42), vbe.h (1.22), vgabios.c (1.53):
+
+ - VBE functions 0x05, 0x06, 0x07 and some dispi ioport functions rewritten in
+ assembler
+ - VBE functions 0x06 and 0x07: get functions now supported, 15 bpp bug fixed
+
+2004-05-05 19:24 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.46), VGABIOS-lgpl-latest.debug.bin (1.46),
+ vbe.c (1.41), vbe.h (1.21), vbe_display_api.txt (1.10), vgabios.c (1.52):
+
+ - 8 bit DAC capability flag set
+ - vbe_biosfn_set_get_dac_palette_format implemented
+ - VBE api description updated
+ - C definitions from header files now used assembler code
+
+2004-05-02 17:27 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.45), VGABIOS-lgpl-latest.debug.bin (1.45),
+ vgabios.c (1.51):
+
+ - text scroll functions for PLANAR1/PLANAR4 graphics modes added
+ - function biosfn_get_ega_info rewritten in assembler
+ - read/write graphics pixel functions rewritten using a case structure
+
+2004-05-01 16:03 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.44), VGABIOS-lgpl-latest.debug.bin (1.44),
+ vgabios.c (1.50):
+
+ - biosfn_enable_cursor_emulation rewritten in assembler
+ - remap of the cursor shape depends on modeset control bit 0
+ - text output in PLANAR4 modes now supports attribute bit 7 (XOR with background)
+
+2004-04-25 20:13 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.43), VGABIOS-lgpl-latest.debug.bin (1.43),
+ vgabios.c (1.49), vgatables.h (1.7):
+
+ - table entries for vga mode 0x0f fixed (PLANAR2 exists on EGA only)
+ - function release_font_access now supports the monochrome text mode
+ - PLANAR1 modes now supported in text output functions and read/write pixel
+ - function AH=0x12/BL=0x32 rewritten in assembler
+
+2004-04-25 08:45 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.42), VGABIOS-lgpl-latest.debug.bin (1.42),
+ vgabios.c (1.48):
+
+ - block address calculation in font functions fixed
+ - functions AX=0x1103, AH=0x12/BL=0x31 and AH=0x12/BL=0x33 rewritten in assembler
+
+2004-04-24 09:59 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.41), VGABIOS-lgpl-latest.debug.bin (1.41),
+ vgabios.c (1.47):
+
+ - read/write graphics pixel for PLANAR4 modes added
+ - CGA specific functions (group AH = 0x0B) implemented
+
+2004-04-23 14:34 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.40), VGABIOS-lgpl-latest.debug.bin (1.40),
+ vgabios.c (1.46):
+
+ - remaining palette and dac read/write functions (except gray scale summing)
+ rewritten in assembler
+
+2004-04-18 13:43 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.39), VGABIOS-lgpl-latest.debug.bin (1.39),
+ vgabios.c (1.45):
+
+ - some palette and dac read/write functions rewritten in assembler
+ - main int10 debug message now works with assembler functions, too
+
+2004-04-18 09:15 japj
+
+ * vbe.c (1.40):
+
+ updated my email address + put vgabios url in the bios copyright string
+ (instead of my old email address)
+
+2004-04-17 07:18 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.38), VGABIOS-lgpl-latest.debug.bin (1.38),
+ vgabios.c (1.44):
+
+ - biosfn_set_video_mode: don't load DAC registers if default palette loading is
+ disabled. Perform gray scale summing if enabled.
+ - biosfn_perform_gray_scale_summing: switch between DAC read and write mode is
+ required to make this function work. Maximum DAC value always set to 0x3f.
+
+2004-04-08 17:50 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.37), VGABIOS-lgpl-latest.debug.bin (1.37),
+ vgabios.c (1.43):
+
+ - write character function for the LINEAR8 mode
+ - get_font_access() and release_font_access() rewritten in assembler
+ - fixed wrong variable name in the init code
+
+2004-04-06 19:31 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.36), VGABIOS-lgpl-latest.debug.bin (1.36),
+ vgabios.c (1.42):
+
+ - init functions rewitten in assembler
+ - function biosfn_set_display_code rewritten in assembler
+
+2004-04-05 19:40 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.35), VGABIOS-lgpl-latest.debug.bin (1.35),
+ vgabios.c (1.41):
+
+ - functions biosfn_get_video_mode() and biosfn_read_display_code() rewritten
+ in assembler
+
+2004-04-04 18:20 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.34), VGABIOS-lgpl-latest.debug.bin (1.34),
+ vgabios.c (1.40):
+
+ - write character function for CGA modes added
+ - read/write graphics pixel for CGA and LINEAR8 modes added
+
+2004-02-23 21:08 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.33), VGABIOS-lgpl-latest.debug.bin (1.33),
+ vbe.c (1.39):
+
+ - dispi_get_max_bpp(): restore the original value of the vbe enable register
+
+2004-02-22 14:17 vruppert
+
+ * README (1.6), vbe.c (1.38), vbe.h (1.20), vbe_display_api.txt (1.9),
+ VGABIOS-lgpl-latest.bin (1.32), VGABIOS-lgpl-latest.debug.bin (1.32):
+
+ - new function dispi_get_max_bpp() returns the bpp capabilities of the Bochs gui
+ - create the mode list depending on the supported bpp capability
+ - unused stuff removed
+ - documentation updated
+
+2004-02-21 18:20 vruppert
+
+ * vbe.c (1.37), vbe.h (1.19), vbetables.h (1.23),
+ VGABIOS-lgpl-latest.bin (1.31), VGABIOS-lgpl-latest.debug.bin (1.31):
+
+ - dynamicly genarated vbe mode_info list works now
+
+2003-11-17 21:04 vruppert
+
+ * vbe.c (1.36), vbetables.h (1.22), vgabios.c (1.39), vgatables.h (1.6),
+ VGABIOS-lgpl-latest.bin (1.30), VGABIOS-lgpl-latest.debug.bin (1.30):
+
+ - new VBE presence flag stored at unused BDA address 0xB9
+ - VBE init code rewritten
+ - added BIOS TTY flag for VBE mode 0x0102 (TODO: scrolling)
+ - vgabios_init_func: load and activate text font already done by set_video_mode
+ - function biosfn_get_all_palette_reg() fixed
+
+2003-11-06 00:26 cbothamy
+
+ * README (1.5):
+
+ - add changes for 0.4c release
+
+2003-11-06 00:22 cbothamy
+
+ * VGABIOS-lgpl-latest.bin (1.29), VGABIOS-lgpl-latest.debug.bin
+ (1.29):
+
+ - compile vgabios.c rev1.38
+
+2003-11-06 00:21 cbothamy
+
+ * vgabios.c (1.38):
+
+ - activate char table after loading it when setting a text video
+ mode
+
+2003-11-06 00:19 cbothamy
+
+ * Makefile (1.12):
+
+ - when making a release, remove unwanted files first, and exclude
+ CVS from the tarball
+
+2003-11-04 22:50 cbothamy
+
+ * ChangeLog (1.20, v0_4b):
+
+ - update ChangeLog for 0.4b release
+
+2003-11-04 22:49 cbothamy
+
+ * README (1.4, v0_4b):
+
+ - update Changes for 0.4b release
+
+2003-11-04 20:26 vruppert
+
+ * vgabios.c (1.37), VGABIOS-lgpl-latest.bin (1.28),
+ VGABIOS-lgpl-latest.debug.bin (1.28) (utags: v0_4b):
+
+ - biosfn_get_font_info(): character height must be returned in CX
+
+2003-11-03 21:57 vruppert
+
+ * vbe.c (1.35, v0_4b), vgabios.c (1.36), VGABIOS-lgpl-latest.bin
+ (1.27), VGABIOS-lgpl-latest.debug.bin (1.27):
+
+ - the 'noclearmem' flag is not stored in the 'current video mode'
+ register (0040h:0049h) - VBE also stores the 'noclear' flag in
+ the 'video control' register (0040h:0087h)
+
+2003-10-05 10:06 vruppert
+
+ * vbe.h (1.18, v0_4b), vbe_display_api.txt (1.8, v0_4b),
+ VGABIOS-lgpl-latest.bin (1.26), VGABIOS-lgpl-latest.debug.bin
+ (1.26):
+
+ - changed VBE i/o registers to 0x01CE/CF (suggestion from Daniel
+ Gimpelevich)
+
+2003-08-18 18:38 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.25), VGABIOS-lgpl-latest.debug.bin
+ (1.25), vgabios.c (1.35):
+
+ - wrong offsets to the character tables (INT 0x1F/0x43) fixed
+ (underscore added) - functions accessing the CRT controller
+ optimized using a local variable 'crtc_addr'
+
+2003-08-17 15:46 cbothamy
+
+ * ChangeLog (1.19, v0_4a):
+
+ - ChangeLog is now automatically generated by running "cvs2cl -r
+ -t -P -S" - update ChangeLog for 0.4a release
+
+2003-08-17 15:44 cbothamy
+
+ * README (1.3, v0_4a):
+
+ - added the old ChangeLog in the HOSTORY section of the README
+ file - update History for 0.4a release, with a summary of Changes
+
+2003-08-17 15:24 cbothamy
+
+ * Makefile (1.11, v0_4b, v0_4a):
+
+ - fix Makefile for "release" target
+
+2003-08-16 01:49 cbothamy
+
+ * Makefile (1.10), README (1.2), VGABIOS-lgpl-latest.bin (1.24,
+ v0_4a), VGABIOS-lgpl-latest.debug.bin (1.24, v0_4a), vgabios.c
+ (1.34, v0_4a):
+
+ - update the Makefile for releases - remove references to old
+ plex86 website - update the Makefile so it build
+ VGABIOS-lgpl-latest.bin and VGABIOS-lgpl-latest.debug.bin
+
+2003-08-07 18:17 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.23), VGABIOS-lgpl-latest.debug.bin
+ (1.23):
+
+ - current VBE mode now stored in BDA (unused address 0xBA)
+
+2003-08-07 17:54 vruppert
+
+ * vbe.c (1.34), vgatables.h (1.5, v0_4b) (utags: v0_4a):
+
+ - current VBE mode now stored in BDA (unused address 0xBA)
+
+2003-07-20 18:05 vruppert
+
+ * vgabios.c (1.33), VGABIOS-lgpl-latest.bin (1.22),
+ VGABIOS-lgpl-latest.debug.bin (1.22):
+
+ - fixed a few functions accessing the attribute controller
+
+2003-07-19 09:33 vruppert
+
+ * vgabios.c (1.32), VGABIOS-lgpl-latest.bin (1.21),
+ VGABIOS-lgpl-latest.debug.bin (1.21):
+
+ - re-enable video after programming the attribute controller -
+ biosfn_set_all_palette_reg(): number of palette registers fixed
+
+2003-07-16 22:32 vruppert
+
+ * ChangeLog (1.18), vbe.c (1.33), vbe.h (1.17, v0_4a),
+ vbe_display_api.txt (1.7, v0_4a), vgabios.c (1.31),
+ VGABIOS-lgpl-latest.bin (1.20), VGABIOS-lgpl-latest.debug.bin
+ (1.20):
+
+ - LFB flag now stored in the register VBE_DISPI_INDEX_ENABLE -
+ release date in Changelog fixed - release date of VBE BIOS 0.6
+ was the same as VGA BIOS 0.3b - year changed in copyright
+ messages
+
+2003-07-15 12:40 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.19), VGABIOS-lgpl-latest.debug.bin
+ (1.19):
+
+ - new function dispi_get_bpp() - function
+ vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp -
+ number of image pages of all VBE modes fixed
+
+2003-07-15 12:35 vruppert
+
+ * vbe.c (1.32), vbetables.h (1.21, v0_4b, v0_4a):
+
+ - new function dispi_get_bpp() - function
+ vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp -
+ number of image pages of all VBE modes fixed
+
+2003-07-14 19:45 vruppert
+
+ * vbe_display_api.txt (1.6):
+
+ - description of VBE_DISPI_ interface 0xb0c2 added
+
+2003-07-10 19:07 vruppert
+
+ * vbe.c (1.31), vbetables.h (1.20), VGABIOS-lgpl-latest.bin (1.18),
+ VGABIOS-lgpl-latest.debug.bin (1.18):
+
+ - 15 bpp VBE modes added - "Bochs own" mode 0x142 (640x480x32bpp)
+ added
+
+2003-07-01 19:00 vruppert
+
+ * vbe.c (1.30), vbe.h (1.16), vbetables.h (1.19),
+ VGABIOS-lgpl-latest.bin (1.17), VGABIOS-lgpl-latest.debug.bin
+ (1.17):
+
+ - VBE preserve display memory feature implemented - VBE mode
+ entries 0x117 and 0x118 added
+
+2003-06-30 21:27 vruppert
+
+ * vbe.c (1.29), vbe.h (1.15), vbetables.h (1.18),
+ VGABIOS-lgpl-latest.bin (1.16), VGABIOS-lgpl-latest.debug.bin
+ (1.16):
+
+ - VBE mode info blocks of modes with >8bpp enabled - VBE modes
+ with 24 bpp: bytes per scanline fixed - vbe_biosfn_set_mode() now
+ supports >8bpp - VBE will be enabled with new VBE_DISPI_ID2
+ (0xB0C2)
+
+2003-06-29 12:53 vruppert
+
+ * vbetables.h (1.17), VGABIOS-lgpl-latest.bin (1.15),
+ VGABIOS-lgpl-latest.debug.bin (1.15):
+
+ - duplicate lines with VBE_MODE_ATTRIBUTE_GRAPHICS_MODE removed -
+ VBE mode info items of currently unsupported modes fixed
+
+2003-06-15 21:19 vruppert
+
+ * vgabios.c (1.30), VGABIOS-lgpl-latest.bin (1.14),
+ VGABIOS-lgpl-latest.debug.bin (1.14):
+
+ - function write_gfx_char() rewritten
+
+2003-04-26 09:27 vruppert
+
+ * VGABIOS-lgpl-latest.debug.bin (1.13):
+
+ - added missing VBE function dispi_get_bank() - added missing
+ return codes for VBE function 4F05h - memory size is always
+ reported in VBE function 4F00h - fixed scan line length for VBE
+ mode 0102h - fixed function set_active_page() for graphics modes
+ - fixed the page sizes of some VGA modes
+
+2003-04-26 09:22 vruppert
+
+ * vbe.c (1.28), vbetables.h (1.16), vgabios.c (1.29), vgatables.h
+ (1.4), VGABIOS-lgpl-latest.bin (1.13):
+
+ - added missing VBE function dispi_get_bank() - added missing
+ return codes for VBE function 4F05h - memory size is always
+ reported in VBE function 4F00h - fixed scan line length for VBE
+ mode 0102h - fixed function set_active_page() for graphics modes
+ - fixed the page sizes of some VGA modes
+
+2003-04-20 09:51 vruppert
+
+ * vgabios.c (1.28), vgatables.h (1.3), VGABIOS-lgpl-latest.bin
+ (1.12), VGABIOS-lgpl-latest.debug.bin (1.12):
+
+ - function write_gfx_char() now supports different font sizes -
+ some entries of the static functionality table fixed
+
+2003-04-18 09:23 vruppert
+
+ * vbe.c (1.27), vbe.h (1.14), vbetables.h (1.15):
+
+ - applied patch #1331 * new function dispi_set_bank_farcall()
+ * VBE mode info item WinFuncPtr points to the new function if the
+ flag VBE_WINDOW_ATTRIBUTE_RELOCATABLE is set * flag
+ VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE added
+
+2003-02-11 20:17 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.11), VGABIOS-lgpl-latest.debug.bin
+ (1.11), vbe.c (1.26), vbetables.h (1.14):
+
+ - VBE mode search rewritten * improved function
+ mode_info_find_mode() is now used by the VBE functions 0x4F01
+ and 0x4F02 * removed all mode list entries with the LFB bit
+ set. LFB detection is now present in the function
+ mode_info_find_mode()
+
+2003-02-09 20:59 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.10), VGABIOS-lgpl-latest.debug.bin
+ (1.10), vgabios.c (1.27):
+
+ - function write_gfx_char(): memory address now calculated in
+ this function; background color is always black - function
+ biosfn_write_char_attr(): the count parameter is now used in
+ graphics modes too - function biosfn_write_char_only() works
+ the same way as function biosfn_write_char_attr() in graphics
+ mode - copying charmap data optimized using memcpyb()
+
+2003-02-09 11:36 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.9), VGABIOS-lgpl-latest.debug.bin
+ (1.9):
+
+ - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA
+ modes with the LFB flag set removed from the list (Linux doesn't
+ like mode numbers > 0x07ff)
+
+2003-02-09 11:02 vruppert
+
+ * vbe.c (1.25), vbe.h (1.13), vbetables.h (1.13):
+
+ - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA
+ modes with the LFB flag set removed from the list (Linux doesn't
+ like mode numbers > 0x07ff)
+
+2003-02-08 13:04 vruppert
+
+ * vbe.c (1.24), vgabios.c (1.26):
+
+ - vbe_biosfn_return_current_mode() now returns the active
+ standard VGA mode TODO: return VESA mode if enabled -
+ biosfn_set_video_mode() now clears the screen in CGA mode
+ correctly - write character functions are now working in all
+ PLANAR4 graphics modes - added stubs for unimplemented features
+ in graphics modes
+
+2003-02-04 22:19 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.8), VGABIOS-lgpl-latest.debug.bin
+ (1.8):
+
+ - set video mode: clear vga memory in graphics mode - set video
+ mode: load default font in text mode - write character
+ implemented for graphics mode 0x12
+
+2003-02-04 22:06 vruppert
+
+ * vgabios.c (1.25):
+
+ - set video mode: clear vga memory in graphics mode - set video
+ mode: load default font in text mode - write character
+ implemented for graphics mode 0x12
+
+2003-01-21 19:30 vruppert
+
+ * vgabios.c (1.24):
+
+ - remap the cursor size if the char height is > 8 and the new
+ values are < 8
+
+2003-01-20 18:24 cbothamy
+
+ * Makefile (1.9):
+
+ - fix so make -j2 does not overwrite temp files
+
+2003-01-19 12:35 vruppert
+
+ * vgabios.c (1.23):
+
+ - function set_scan_lines() recalculates the number of rows and
+ the page size - new values for char height, text rows and page
+ size are stored in the BIOS data segment - asm helper function
+ idiv_u added
+
+2003-01-15 18:49 cbothamy
+
+ * VGABIOS-lgpl-latest.bin (1.7), VGABIOS-lgpl-latest.debug.bin
+ (1.7):
+
+ - compile vgabios rev 1.22
+
+2003-01-15 18:49 cbothamy
+
+ * vgabios.c (1.22):
+
+ - fix bug found by ams : a 8bits index value was compared to
+ 0x100 in some cases in biosfn_set_all_dac_reg,
+ biosfn_read_all_dac_reg, biosfn_perform_gray_scale_summing
+
+2003-01-15 17:34 cbothamy
+
+ * Makefile (1.8):
+
+ - fix symbol table file names, discovered by ams
+
+2003-01-04 21:20 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.6), VGABIOS-lgpl-latest.debug.bin
+ (1.6), vgabios.c (1.21):
+
+ - biosfn_set_video_mode(): reset attribute controller flip-flop
+ before setting up the controller's registers (bug found with
+ amidiag)
+
+2003-01-04 09:50 vruppert
+
+ * vbe.c (1.23):
+
+ - VBE function 0x00 returns VBE 1.x compatible information if no
+ VBE signature is present
+
+2003-01-01 12:44 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.5), VGABIOS-lgpl-latest.debug.bin
+ (1.5):
+
+ - SVGA mode 0x6A (800x600x4) added to the list of graphics modes
+
+2002-12-31 18:07 vruppert
+
+ * vgatables.h (1.2):
+
+ - SVGA mode 0x6A (800x600x4) added to the list of graphics modes
+
+2002-11-23 10:38 cbothamy
+
+ * ChangeLog (1.17, v0_3b):
+
+ - fix changelog for 0.3b release
+
+2002-10-20 17:12 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.4), VGABIOS-lgpl-latest.debug.bin
+ (1.4), vgabios.c (1.20) (utags: v0_3b):
+
+ - new function set_scan_lines() for the font size change (patch
+ from Hartmut Birr) - cursor shape start and end must be updated
+ in set_scan_lines() - set_scan_lines() is called by the functions
+ 0x1110, 0x1111, 0x1112 and 0x1114 after copying the font data
+
+2002-10-04 08:20 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.3), VGABIOS-lgpl-latest.debug.bin
+ (1.3), vgabios.c (1.19):
+
+ - biosfn_set_single_dac_reg(): the red value is stored in DH
+
+2002-09-19 19:05 cbothamy
+
+ * VGABIOS-lgpl-latest.bin (1.2), VGABIOS-lgpl-latest.debug.bin
+ (1.2):
+
+ - updated with latest changes
+
+2002-09-19 19:03 cbothamy
+
+ * ChangeLog (1.16), Makefile (1.7, v0_3b), vbe.c (1.22, v0_3b),
+ vgabios.c (1.18), vgabios.h (1.3, v0_4b, v0_4a, v0_3b):
+
+ - updated the Makefile - removed display of copyrights. -
+ changed the Copyright string to "LGPL VGABios developers"
+
+2002-09-08 21:14 vruppert
+
+ * vgabios.c (1.17):
+
+ - set the cursor shape depending on the current font height -
+ clear BL before calling int 0x10 function 0x1103 in
+ vgabios_init_func
+
+2002-08-23 22:58 cbothamy
+
+ * vbe.c (1.21), vbetables.h (1.12, v0_3b):
+
+ - added lfb-mode numbers (patch from mathis)
+
+2002-07-21 21:57 japj
+
+ * vbe.c (1.20), vgabios.c (1.16):
+
+ gcc2/3 preprocessing fix
+
+2002-05-18 16:55 cbothamy
+
+ * vgabios.c (1.15):
+
+ - include patch from Volker that adds some text font functions
+
+2002-05-01 23:13 japj
+
+ * VGABIOS-lgpl-latest.bin (1.1), VGABIOS-lgpl-latest.debug.bin
+ (1.1):
+
+ adding latest bin & debug bin of the vgabios
+
+2002-04-29 14:50 japj
+
+ * ChangeLog (1.15), vbe.c (1.19), vbe.h (1.12, v0_3b), vbetables.h
+ (1.11), vgabios.c (1.14):
+
+ - applying hw scrolling/multibuffering patch
+
+2002-04-25 21:59 japj
+
+ * Makefile (1.6), vbe.c (1.18), vgabios.c (1.13):
+
+ - reverting #asm/##asm & endasm patch (does not work with with
+ cygwin)
+
+2002-04-19 19:38 japj
+
+ * Makefile (1.5), vbe.c (1.17), vgabios.c (1.12):
+
+ - fixing preprocessing of vgabios with latest gcc (from Mandrake
+ 8.2)
+
+2002-04-08 23:44 japj
+
+ * ChangeLog (1.14), vbe_display_api.txt (1.5, v0_3b):
+
+ - preparing docs for new DISPI interface (for hardware scrolling)
+
+2002-04-03 19:06 japj
+
+ * ChangeLog (1.13), TODO (1.9, v0_4b, v0_4a, v0_3b), vbe.c (1.16):
+
+ - defaulting LFB on + updated changelog & todo
+
+2002-04-03 00:38 cbothamy
+
+ * vbe.c (1.15), vgabios.c (1.11):
+
+ - changed the logging ports to 0x500 -> 0x502
+
+2002-03-14 17:54 japj
+
+ * vbe.c (1.14):
+
+ - vbetables.h is dependant upon some defines (VBE_HAVE_LFB), so
+ put the include *after* the define
+
+2002-03-13 21:47 japj
+
+ * ChangeLog (1.12), TODO (1.8), vbe.c (1.13), vbetables.h (1.10),
+ vgabios.c (1.10):
+
+ - made LFB dependant upon define - not implement vbe functions
+ return failure - updated todo & docs for things after bochs 1.4
+
+2002-03-13 19:46 japj
+
+ * vbe.h (1.11), vbe_display_api.txt (1.4):
+
+ - added max video memory + documented what is in the 0xb0c0
+ interface
+
+2002-03-12 02:33 cbothamy
+
+ * ChangeLog (1.11), Makefile (1.4):
+
+ - updated for 0.3a. Merged vgabios.bin and vbebios.bin
+
+2002-03-10 21:36 japj
+
+ * ChangeLog (1.10), vbetables.h (1.9):
+
+ - added LFB modes for testing with vbe-lfb patch in Bochs
+
+2002-03-10 17:42 japj
+
+ * vbe.c (1.12, v0_3a):
+
+ - show people when they do NOT have VBE support available
+
+2002-03-10 17:36 japj
+
+ * TODO (1.7, v0_3a), vbe.c (1.11), vbe.h (1.10, v0_3a), vgabios.c
+ (1.9, v0_3a):
+
+ - cleanup of vbe internal functions (set 8bpp mode is now
+ dependant on ModeInfo content instead of hardcoded functions)
+
+2002-03-10 17:20 cbothamy
+
+ * ChangeLog (1.9, v0_3a), TODO (1.6):
+
+ - updated for 0.3a
+
+2002-03-10 17:19 cbothamy
+
+ * vbe.c (1.10), vbe.h (1.9):
+
+ - added vbe_has_vbe_display function that detects an attached vbe
+ display
+
+2002-03-10 17:12 cbothamy
+
+ * vgabios.c (1.8):
+
+ - vbe calls are done only if a vbe display is detected
+
+2002-03-10 11:25 japj
+
+ * vbe.h (1.8), vbe_display_api.txt (1.3, v0_3a):
+
+ - preparing for LFB support
+
+2002-03-09 14:25 japj
+
+ * vgabios.c (1.7):
+
+ - fixing initial cursor shape to _ instead of -
+
+2002-03-08 23:08 japj
+
+ * ChangeLog (1.8), TODO (1.5), vbe.c (1.9), vbe.h (1.7), vgabios.c
+ (1.6):
+
+ - updating vbe code to new API
+
+2002-03-08 21:48 japj
+
+ * vbe.c (1.8), vbe.h (1.6), vbetables.h (1.8, v0_3a):
+
+ - updating vbe code with #defines from API
+
+2002-03-08 21:31 japj
+
+ * vbe_display_api.txt (1.2):
+
+ - adding some text about how banks work
+
+2002-03-08 21:09 japj
+
+ * ChangeLog (1.7), vbe_display_api.txt (1.1):
+
+ - adding vbe_display_api documentation
+
+2002-03-07 21:36 japj
+
+ * ChangeLog (1.6), vbe.c (1.7), vbetables.h (1.7):
+
+ - added 1024x768xbpp support - some more cleanups/comments
+
+2002-03-06 21:55 japj
+
+ * ChangeLog (1.5), TODO (1.4), vbe.c (1.6), vbetables.h (1.6),
+ vgabios.c (1.5):
+
+ - updated changelog with new modi - added 640x480x8 (Mandrake
+ Installer can use this!) - added pre VBE2 compatible 'detection'
+ - fixed problem when normal vga set mode wouldn't disable vbe
+ mode
+
+2002-03-06 20:59 japj
+
+ * TODO (1.3), vbe.c (1.5), vbe.h (1.5), vbetables.h (1.5),
+ vgabios.c (1.4):
+
+ - adding 640x400x8 and 800x600x8 vbe support (this depends
+ HEAVILY on my bochs vga code patch - japj)
+
+2002-03-06 18:00 japj
+
+ * vbe.c (1.4), vbe.h (1.4), vbetables.h (1.4):
+
+ - implemented banked & lfb support for 320x200x8bpp (some fixes
+ for vbetest program not displaying anything)
+
+2002-03-05 20:25 japj
+
+ * Makefile (1.3, v0_3a):
+
+ for vbe debug bios: - print debugging information in assembly
+ output - print source code in assembly output
+
+2002-03-01 19:39 japj
+
+ * ChangeLog (1.4), TODO (1.2), vbe.c (1.3), vbe.h (1.3),
+ vbetables.h (1.3):
+
+ - added vbe support for 320x200x8 using the standard vgamode
+ (0x13)
+
+2002-02-19 00:29 japj
+
+ * ChangeLog (1.3):
+
+ - updating ChangeLog with lfbprof
+
+2002-02-18 23:26 japj
+
+ * tests/lfbprof/: lfbprof.c (1.2), lfbprof.h (1.2) (utags: v0_3a,
+ v0_3b, v0_4a, v0_4b):
+
+ - fixed unsigned short for mode list (-1 != 0xffff otherwise) -
+ fixed LfbMapRealPointer macro mask problem (some modes were
+ skipped) - added some extra 'debugging' printf's
+
+2002-02-18 23:07 japj
+
+ * tests/lfbprof/: Makefile (1.1, v0_4b, v0_4a, v0_3b, v0_3a),
+ lfbprof.c (1.1), lfbprof.h (1.1):
+
+ - Adding lfbprof testprogram (for vbe testing purposes) It
+ needs to be compiled with the Watcom C Compiler
+
+2002-02-18 18:48 japj
+
+ * vbe.c (1.2), vbe.h (1.2):
+
+ - cosmetic updates to vbe.c/h + added bunch of FIXMEs for work
+ that needs to be done
+
+2002-02-18 18:34 japj
+
+ * vbetables.h (1.2):
+
+ - cosmetic updates in vbetables.h
+
+2002-02-18 18:32 japj
+
+ * ChangeLog (1.2):
+
+ updated changelog with merge of vbebios 0.2
+
+2002-02-18 18:07 japj
+
+ * vgabios.c (1.3):
+
+ - small cosmetic cleanup in vgabios vbe code + added FIXMEs
+
+2002-02-18 17:55 japj
+
+ * Makefile (1.2), dataseghack (1.2, v0_4b, v0_4a, v0_3b, v0_3a),
+ vbe.c (1.1), vbe.h (1.1), vbetables.h (1.1), vgabios.c (1.2),
+ vgabios.h (1.2, v0_3a):
+
+ - merging with vbebios 0.2 release
+
+2002-02-18 11:31 cbothamy
+
+ * BUGS (1.1, v0_4b, v0_4a, v0_3b, v0_3a), COPYING (1.1, v0_4b,
+ v0_4a, v0_3b, v0_3a), ChangeLog (1.1), Makefile (1.1), Notes
+ (1.1, v0_4b, v0_4a, v0_3b, v0_3a), README (1.1, v0_3b, v0_3a),
+ TODO (1.1), dataseghack (1.1), vgabios.c (1.1), vgabios.h (1.1),
+ vgafonts.h (1.1, v0_4b, v0_4a, v0_3b, v0_3a), vgatables.h (1.1,
+ v0_3b, v0_3a), tests/testbios.c (1.1, v0_4b, v0_4a, v0_3b,
+ v0_3a):
+
+ - initial import
+
diff --git a/kvm/vgabios/Makefile b/kvm/vgabios/Makefile
new file mode 100644
index 000000000..00e8c6687
--- /dev/null
+++ b/kvm/vgabios/Makefile
@@ -0,0 +1,87 @@
+SHELL = /bin/sh
+
+CC = gcc
+CFLAGS = -g -O2 -Wall -Wstrict-prototypes
+LDFLAGS =
+
+GCC = gcc
+BCC = bcc
+AS86 = as86
+
+RELEASE = `pwd | sed "s-.*/--"`
+RELDATE = `date '+%d %b %Y'`
+RELVERS = `pwd | sed "s-.*/--" | sed "s/vgabios//" | sed "s/-//"`
+
+VGABIOS_DATE = "-DVGABIOS_DATE=\"$(RELDATE)\""
+
+all: bios cirrus-bios
+
+
+bios: biossums vgabios.bin vgabios.debug.bin
+
+cirrus-bios: vgabios-cirrus.bin vgabios-cirrus.debug.bin
+
+clean:
+ /bin/rm -f biossums vbetables-gen vbetables.h *.o *.s *.ld86 \
+ temp.awk.* vgabios*.orig _vgabios_* _vgabios-debug_* core vgabios*.bin vgabios*.txt $(RELEASE).bin *.bak
+
+dist-clean: clean
+
+release:
+ VGABIOS_VERS=\"-DVGABIOS_VERS=\\\"$(RELVERS)\\\"\" make bios cirrus-bios
+ /bin/rm -f *.o *.s *.ld86 \
+ temp.awk.* vgabios.*.orig _vgabios_.*.c core *.bak .#*
+ cp VGABIOS-lgpl-latest.bin ../$(RELEASE).bin
+ cp VGABIOS-lgpl-latest.debug.bin ../$(RELEASE).debug.bin
+ cp VGABIOS-lgpl-latest.cirrus.bin ../$(RELEASE).cirrus.bin
+ cp VGABIOS-lgpl-latest.cirrus.debug.bin ../$(RELEASE).cirrus.debug.bin
+ tar czvf ../$(RELEASE).tgz --exclude CVS -C .. $(RELEASE)/
+
+vgabios.bin: vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE $(VGABIOS_DATE) > _vgabios_.c
+ $(BCC) -o vgabios.s -C-c -D__i86__ -S -0 _vgabios_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios.s > _vgabios_.s
+ $(AS86) _vgabios_.s -b vgabios.bin -u -w- -g -0 -j -O -l vgabios.txt
+ rm -f _vgabios_.s _vgabios_.c vgabios.s
+ mv vgabios.bin VGABIOS-lgpl-latest.bin
+ ./biossums VGABIOS-lgpl-latest.bin
+ ls -l VGABIOS-lgpl-latest.bin
+
+vgabios.debug.bin: vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE -DDEBUG $(VGABIOS_DATE) > _vgabios-debug_.c
+ $(BCC) -o vgabios-debug.s -C-c -D__i86__ -S -0 _vgabios-debug_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios-debug.s > _vgabios-debug_.s
+ $(AS86) _vgabios-debug_.s -b vgabios.debug.bin -u -w- -g -0 -j -O -l vgabios.debug.txt
+ rm -f _vgabios-debug_.s _vgabios-debug_.c vgabios-debug.s
+ mv vgabios.debug.bin VGABIOS-lgpl-latest.debug.bin
+ ./biossums VGABIOS-lgpl-latest.debug.bin
+ ls -l VGABIOS-lgpl-latest.debug.bin
+
+vgabios-cirrus.bin: vgabios.c vgabios.h vgafonts.h vgatables.h clext.c
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus_.c
+ $(BCC) -o vgabios-cirrus.s -C-c -D__i86__ -S -0 _vgabios-cirrus_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus.s > _vgabios-cirrus_.s
+ $(AS86) _vgabios-cirrus_.s -b vgabios-cirrus.bin -u -w- -g -0 -j -O -l vgabios.cirrus.txt
+ rm -f _vgabios-cirrus_.s _vgabios-cirrus_.c vgabios-cirrus.s
+ mv vgabios-cirrus.bin VGABIOS-lgpl-latest.cirrus.bin
+ ./biossums VGABIOS-lgpl-latest.cirrus.bin
+ ls -l VGABIOS-lgpl-latest.cirrus.bin
+
+vgabios-cirrus.debug.bin: vgabios.c vgabios.h vgafonts.h vgatables.h clext.c
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DCIRRUS_DEBUG -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus-debug_.c
+ $(BCC) -o vgabios-cirrus-debug.s -C-c -D__i86__ -S -0 _vgabios-cirrus-debug_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus-debug.s > _vgabios-cirrus-debug_.s
+ $(AS86) _vgabios-cirrus-debug_.s -b vgabios.cirrus.debug.bin -u -w- -g -0 -j -O -l vgabios.cirrus.debug.txt
+ rm -f _vgabios-cirrus-debug_.s _vgabios-cirrus-debug_.c vgabios-cirrus-debug.s
+ mv vgabios.cirrus.debug.bin VGABIOS-lgpl-latest.cirrus.debug.bin
+ ./biossums VGABIOS-lgpl-latest.cirrus.debug.bin
+ ls -l VGABIOS-lgpl-latest.cirrus.debug.bin
+
+biossums: biossums.c
+ $(CC) -o biossums biossums.c
+
+vbetables-gen: vbetables-gen.c
+ $(CC) -o vbetables-gen vbetables-gen.c
+
+vbetables.h: vbetables-gen
+ ./vbetables-gen > $@
diff --git a/kvm/vgabios/Notes b/kvm/vgabios/Notes
new file mode 100644
index 000000000..d5b708dc7
--- /dev/null
+++ b/kvm/vgabios/Notes
@@ -0,0 +1,11 @@
+Development notes
+-----------------
+
+- need to split video init function
+ 1. set bios variables
+ 2. do the real init with io based on bios variables
+
+- characters format switching will set the bios
+ variables and call function #2 above
+
+- need to rework the tables as explained in Interrupt list
diff --git a/kvm/vgabios/README b/kvm/vgabios/README
new file mode 100644
index 000000000..90141d426
--- /dev/null
+++ b/kvm/vgabios/README
@@ -0,0 +1,219 @@
+Plex86/Bochs VGABios
+--------------------
+
+The goal of this project is to have a LGPL'd Video Bios in plex86,
+Bochs and qemu.
+This VGA Bios is very specific to the emulated VGA card.
+It is NOT meant to drive a physical vga card.
+
+
+Cirrus SVGA extension
+---------------------
+
+The Cirrus SVGA extension is designed for the Cirrus emulation in Bochs and
+qemu. The initial patch for the Cirrus extension has been written by Makoto
+Suzuki (suzu).
+
+
+Install
+-------
+To compile the VGA Bios you will need :
+- gcc
+- bcc
+- as86
+- ld86
+
+Untar the archive, and type make. You should get a "VGABIOS-lgpl-latest.bin"
+file. Alternatively, you can use the binary file "VGABIOS-lgpl-latest.bin",
+i have compiled for you.
+
+Edit your plex86/bochs conf file, and modify the load-rom command in the
+VGA BIOS section, to point to the new vgabios image file.
+
+
+Debugging
+---------
+You can get a very basic debugging system: messages printed by the vgabios.
+You have to register the "unmapped" device driver in plex86 or bochs, and make
+sure it grabs port 0xfff0.
+
+Comment the #undef DEBUG at the beginning of vgabios.c.
+You can then use the "printf" function in the bios.
+
+
+Testing
+-------
+Look at the "testvga.c" file in the archive. This is a minimal Turbo C 2.0
+source file that calls a few int10 functions. Feel free to modify it to suit
+your needs.
+
+
+Copyright and License
+---------------------
+This program has been written by Christophe Bothamy
+It is protected by the GNU Lesser Public License, which you should
+have received a copy of along with this package.
+
+
+Reverse Engineering
+-------------------
+The VGA Bios has been written without reverse-engineering any existing Bios.
+
+
+Acknowledgment
+--------------
+The source code contains code ripped from rombios.c of plex86, written
+by Kevin Lawton <kevin2001@yahoo.com>
+
+The source code contains fonts from fntcol16.zip (c) by Joseph Gil avalable at :
+ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+These fonts are public domain
+
+The source code is based on information taken from :
+- Kevin Lawton's vga card emulation for bochs/plex86
+- Ralf Brown's interrupts list avalaible at
+ http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
+- Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/
+- Michael Abrash's Graphics Programming Black Book
+- Francois Gervais' book "programmation des cartes graphiques cga-ega-vga"
+ edited by sybex
+- DOSEMU 1.0.1 source code for several tables values and formulas
+
+
+Feedback
+--------
+Please report any bugs, comments, patches for this VGA Bios to info@vruppert.de
+You can find the latest release at : http://www.nongnu.org/vgabios/
+For any information on bochs, visit the website http://bochs.sourceforge.net/
+For any information on qemu, visit the website http://fabrice.bellard.free.fr/qemu/
+
+
+History
+-------
+vgabios-0.6b : May 30 2008
+ - Volker
+ . added PCI data structure for the Cirrus VGABIOS images
+ . minor bugfixes in biossums utility, VBE support and makefile
+
+vgabios-0.6a : Aug 19 2006
+ - Volker
+ . added minimal support for the video parameter table (VPT)
+ . Cirrus SVGA now supports the "no clear" bit in Cirrus and VESA mode
+ . Bochs VBE protected mode interface improved
+ . save/restore video state support for Bochs VBE and standard VGA added
+ . generate vbetables.h dynamicly
+ . VBE video memory increased to 8 MB (VBE dispi ID changed to B0C4)
+ . lots of 4bpp VBE fixes (all 4bpp VBE modes now enabled)
+ . VGA compatible setup for VBE modes added
+
+vgabios-0.5d : Dec 29 2005
+ - Volker
+ . Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com)
+ . biossums utility now supports VGABIOS sizes up to 64 kBytes
+ . VGA mode 0x11: all color planes must be enabled in this 2-color VGA mode
+
+vgabios-0.5c : Jul 07 2005
+ - Volker
+ . BIOS configuration word usually reports initial mode 80x25 color text
+ . vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the
+ cursor row value
+
+vgabios-0.5b : May 24 2005
+ - Volker
+ . fixed return value for the default case in the VBE section (non-debug mode)
+ . removed unused stuff
+
+vgabios-0.5a : Mar 07 2005
+ - Volker
+ . Cirrus SVGA extension (initial patches from Makoto Suzuki, improvements
+ from Fabrice Bellard)
+ . vgabios image size is now exactly 32k with a checksum
+ . a lot of vgabios and vbe functions rewritten in assembler
+ . dynamicly generated VBE mode info list
+ . write character function for CGA and LINEAR8 modes
+ . read/write graphics pixel for some graphics modes
+ . text scroll feature for some graphics modes
+ . VBE 8-bit DAC support
+
+vgabios-0.4c : Nov 06 2003
+ - Christophe
+ . fix font problem on initial screen of NT4 Loader
+
+vgabios-0.4b : Nov 04 2003
+ - Volker
+ . fix offset of character tables
+ . optimizations of CRT controller accesses
+ . VBE i/o registers changed to 0x01CE/CF
+ (suggestion from Daniel Gimpelevich)
+ . "noclear" flag stored in BIOS area
+ . fix character height returned by get_font_info function
+
+vgabios-0.4a : Aug 17 2003
+ - Volker
+ . VBE mode search rewritten (VBE modes with LFB bit removed)
+ . many bugfixes and optimizations
+ . write character function implemented for graphics modes
+ . support for 15bpp, 16bpp, 24bpp and 32bpp VBE modes added
+ . SVGA mode 0x6A added
+ . VBE modes 0x102, 0x117, 0x118 and 0x142 (Bochs specific)
+
+vgabios-0.3b : Nov 23 2002
+ - Christophe
+ . added lfb-mode numbers (patch from mathis)
+ . updated the Makefile
+ . removed display of copyrights.
+ . changed the Copyright string to "LGPL VGABios developers"
+ - Volker
+ . set the cursor shape depending on the current font height
+ . clear BL before calling int 0x10 function 0x1103 in vgabios_init_func
+ . added some text font functions
+ - Jeroen
+ . Forced to new DISPI (0xb0c1) interface (requires latest bochs vbe code)
+ . Added multibuffering support
+ . Added new DISPI interface for: virt width, height, x offset, y offset
+ . Added LFB modes (to be used with the vbe-lfb patch in bochs)
+ see VBE_HAVE_LFB in vbe.c (currently default enabled)
+ . updated TODO & docs for changes after bochs 1.4
+
+vgabios-0.3a : Mar 10 2002
+ - Christophe
+ . Fixed bug in function ah=13
+ - Jeroen
+ . updated vbebios implementation to new api
+ . added vbe_display_api documentation
+ . added 640x400x8, 640x480x8, 800x600x8, 1024x768
+ (>640x480 needs a special bochs patch atm)
+ . added 320x200x8 vbe support (uses the standard 320x200x8 vga mode to
+ display, this allows for testing & having something on screen as well,
+ at least until bochs host side display is up & running)
+ . adding lfbprof (vbe) testprogram (+some small fixes to it)
+ . merging with vbebios 0.2
+
+vgabios-0.2b : Nov 19 2001
+ - Christophe
+ . Fixed bug in function ah=13
+
+vgabios-0.2a : Nov 09 2001
+ - Christophe
+ . Included bugfix from techt@pikeonline.net about grayscale summing
+ . Added the "IBM" string at org 0x1e as Bart Oldeman suggested
+ . Fixed DS and ES that where inverted in the int10 parameters list!
+ . The following have been implemented :
+ - function ax=1a00, ax=1a01, ah=1b
+ - function ax=1130
+ . Added debug messages for unimplemented/unknown functions
+ Must be compiled with DEBUG defined. The output is trapped
+ by the unknown-ioport driver of plex/bochs (port 0xfff0 is used)
+
+vgabios-0.1a : May 8 2001
+ - Christophe
+ . First release. The work has been focused only on text mode.
+ . The following have been implemented :
+ - inits
+ - int 10 handler
+ - functions ah=00, ah=01, ah=02, ah=03, ah=05, ah=06, ah=07, ah=08
+ ah=09, ah=0a, ah=0e, ah=0f, ax=1000, ax=1001, ax=1002, ax=1003
+ ax=1007, ax=1008, ax=1009, ax=1010, ax=1012, ax=1013, ax=1015
+ ax=1017, ax=1018, ax=1019, ax=101a, ax=101b, ah=12 bl=10,
+ ah=12 bl=30, ah=12 bl=31, ah=12 bl=32, ah=12 bl=33, ah=12 bl=34
+ ah=13
diff --git a/kvm/vgabios/TODO b/kvm/vgabios/TODO
new file mode 100644
index 000000000..b08ee4b77
--- /dev/null
+++ b/kvm/vgabios/TODO
@@ -0,0 +1,26 @@
+Short term :
+------------
+
+General
+ - Fix init mode (ah=00). Should use more BIOS variables
+ - Add new functionalities and modify static functionality table
+ - Performance : 16 bits IO
+
+v0.7
+ - Implement the remaining functions (don't know if all are needed):
+ - chargen ax=1120, ax=1121, ax=1122, ax=1123, ax=1124
+ - display switch interface ah=12 bl=35
+ - video refresh control ah=12 bl=36
+ - Graphic modes
+
+v1.0
+ - Bugfixes
+
+
+=================================================================================================
+VBE:
+----
+Long term:
+- have plex86 host side display interface
+- have text io functions in vbe mode
+
diff --git a/kvm/vgabios/biossums.c b/kvm/vgabios/biossums.c
new file mode 100644
index 000000000..d5816f420
--- /dev/null
+++ b/kvm/vgabios/biossums.c
@@ -0,0 +1,282 @@
+/* biossums.c --- written by Eike W. for the Bochs BIOS */
+/* adapted for the LGPL'd VGABIOS by vruppert */
+
+/* This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef unsigned char byte;
+
+void check( int value, char* message );
+
+#define MAX_BIOS_DATA 0x10000
+
+long chksum_bios_get_offset( byte* data, long offset );
+byte chksum_bios_calc_value( byte* data, long offset );
+byte chksum_bios_get_value( byte* data, long offset );
+void chksum_bios_set_value( byte* data, long offset, byte value );
+
+#define PMID_LEN 20
+#define PMID_CHKSUM 19
+
+long chksum_pmid_get_offset( byte* data, long offset );
+byte chksum_pmid_calc_value( byte* data, long offset );
+byte chksum_pmid_get_value( byte* data, long offset );
+void chksum_pmid_set_value( byte* data, long offset, byte value );
+
+#define PCIR_LEN 24
+
+long chksum_pcir_get_offset( byte* data, long offset );
+
+
+byte bios_data[MAX_BIOS_DATA];
+long bios_len;
+
+
+int main(int argc, char* argv[])
+{
+ FILE* stream;
+ long offset, tmp_offset, pcir_offset;
+ byte bios_len_byte, cur_val = 0, new_val = 0;
+ int hits, modified;
+
+ if (argc != 2) {
+ printf( "Error. Need a file-name as an argument.\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ if ((stream = fopen(argv[1], "rb")) == NULL) {
+ printf("Error opening %s for reading.\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ memset(bios_data, 0, MAX_BIOS_DATA);
+ bios_len = fread(bios_data, 1, MAX_BIOS_DATA, stream);
+ if (bios_len > MAX_BIOS_DATA) {
+ printf("Error reading max. 65536 Bytes from %s.\n", argv[1]);
+ fclose(stream);
+ exit(EXIT_FAILURE);
+ }
+ fclose(stream);
+ modified = 0;
+ if (bios_len < 0x8000) {
+ bios_len = 0x8000;
+ modified = 1;
+ } else if ((bios_len & 0x1FF) != 0) {
+ bios_len = (bios_len + 0x200) & ~0x1FF;
+ modified = 1;
+ }
+ bios_len_byte = (byte)(bios_len / 512);
+ if (bios_len_byte != bios_data[2]) {
+ if (modified == 0) {
+ bios_len += 0x200;
+ }
+ bios_data[2] = (byte)(bios_len / 512);
+ modified = 1;
+ }
+
+ hits = 0;
+ offset = 0L;
+ while( (tmp_offset = chksum_pmid_get_offset( bios_data, offset )) != -1L ) {
+ offset = tmp_offset;
+ cur_val = chksum_pmid_get_value( bios_data, offset );
+ new_val = chksum_pmid_calc_value( bios_data, offset );
+ printf( "\nPMID entry at: 0x%4lX\n", offset );
+ printf( "Current checksum: 0x%02X\n", cur_val );
+ printf( "Calculated checksum: 0x%02X ", new_val );
+ hits++;
+ }
+ if ((hits == 1) && (cur_val != new_val)) {
+ printf("Setting checksum.");
+ chksum_pmid_set_value( bios_data, offset, new_val );
+ if (modified == 0) {
+ bios_len += 0x200;
+ bios_data[2]++;
+ }
+ modified = 1;
+ }
+ if (hits >= 2) {
+ printf( "Multiple PMID entries! No checksum set." );
+ }
+ if (hits) {
+ printf("\n");
+ }
+
+ offset = 0L;
+ pcir_offset = chksum_pcir_get_offset( bios_data, offset );
+ if (pcir_offset != -1L) {
+ if (bios_data[pcir_offset + 16] != bios_data[2]) {
+ bios_data[pcir_offset + 16] = bios_data[2];
+ if (modified == 0) {
+ bios_len += 0x200;
+ bios_data[2]++;
+ bios_data[pcir_offset + 16]++;
+ }
+ modified = 1;
+ }
+ }
+
+ offset = 0L;
+ do {
+ offset = chksum_bios_get_offset(bios_data, offset);
+ cur_val = chksum_bios_get_value(bios_data, offset);
+ new_val = chksum_bios_calc_value(bios_data, offset);
+ if ((cur_val != new_val) && (modified == 0)) {
+ bios_len += 0x200;
+ bios_data[2]++;
+ if (pcir_offset != -1L) {
+ bios_data[pcir_offset + 16]++;
+ }
+ modified = 1;
+ } else {
+ printf("\nBios checksum at: 0x%4lX\n", offset);
+ printf("Current checksum: 0x%02X\n", cur_val);
+ printf("Calculated checksum: 0x%02X ", new_val);
+ if (cur_val != new_val) {
+ printf("Setting checksum.");
+ chksum_bios_set_value(bios_data, offset, new_val);
+ cur_val = new_val;
+ modified = 1;
+ }
+ printf( "\n" );
+ }
+ } while (cur_val != new_val);
+
+ if (modified == 1) {
+ if ((stream = fopen( argv[1], "wb")) == NULL) {
+ printf("Error opening %s for writing.\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ if (fwrite(bios_data, 1, bios_len, stream) < bios_len) {
+ printf("Error writing %d KBytes to %s.\n", bios_len / 1024, argv[1]);
+ fclose(stream);
+ exit(EXIT_FAILURE);
+ }
+ fclose(stream);
+ }
+
+ return (EXIT_SUCCESS);
+}
+
+
+void check( int okay, char* message ) {
+
+ if( !okay ) {
+ printf( "\n\nError. %s.\n", message );
+ exit( EXIT_FAILURE );
+ }
+}
+
+
+long chksum_bios_get_offset( byte* data, long offset ) {
+
+ return (bios_len - 1);
+}
+
+
+byte chksum_bios_calc_value( byte* data, long offset ) {
+
+ int i;
+ byte sum;
+
+ sum = 0;
+ for( i = 0; i < offset; i++ ) {
+ sum = sum + *( data + i );
+ }
+ sum = -sum; /* iso ensures -s + s == 0 on unsigned types */
+ return( sum );
+}
+
+
+byte chksum_bios_get_value( byte* data, long offset ) {
+
+ return( *( data + offset ) );
+}
+
+
+void chksum_bios_set_value( byte* data, long offset, byte value ) {
+
+ *( data + offset ) = value;
+}
+
+
+byte chksum_pmid_calc_value( byte* data, long offset ) {
+
+ int i;
+ int len;
+ byte sum;
+
+ len = PMID_LEN;
+ check((offset + len) <= (bios_len - 1), "PMID entry length out of bounds" );
+ sum = 0;
+ for( i = 0; i < len; i++ ) {
+ if( i != PMID_CHKSUM ) {
+ sum = sum + *( data + offset + i );
+ }
+ }
+ sum = -sum;
+ return( sum );
+}
+
+
+long chksum_pmid_get_offset( byte* data, long offset ) {
+
+ long result = -1L;
+
+ while ((offset + PMID_LEN) < (bios_len - 1)) {
+ offset = offset + 1;
+ if( *( data + offset + 0 ) == 'P' && \
+ *( data + offset + 1 ) == 'M' && \
+ *( data + offset + 2 ) == 'I' && \
+ *( data + offset + 3 ) == 'D' ) {
+ result = offset;
+ break;
+ }
+ }
+ return( result );
+}
+
+
+byte chksum_pmid_get_value( byte* data, long offset ) {
+
+ check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" );
+ return( *( data + offset + PMID_CHKSUM ) );
+}
+
+
+void chksum_pmid_set_value( byte* data, long offset, byte value ) {
+
+ check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" );
+ *( data + offset + PMID_CHKSUM ) = value;
+}
+
+
+long chksum_pcir_get_offset( byte* data, long offset ) {
+
+ long result = -1L;
+
+ while ((offset + PCIR_LEN) < (bios_len - 1)) {
+ offset = offset + 1;
+ if( *( data + offset + 0 ) == 'P' && \
+ *( data + offset + 1 ) == 'C' && \
+ *( data + offset + 2 ) == 'I' && \
+ *( data + offset + 3 ) == 'R' ) {
+ result = offset;
+ break;
+ }
+ }
+ return( result );
+}
diff --git a/kvm/vgabios/clext.c b/kvm/vgabios/clext.c
new file mode 100644
index 000000000..c7a2ad0ef
--- /dev/null
+++ b/kvm/vgabios/clext.c
@@ -0,0 +1,1688 @@
+//
+// QEMU Cirrus CLGD 54xx VGABIOS Extension.
+//
+// Copyright (c) 2004 Makoto Suzuki (suzu)
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+
+//#define CIRRUS_VESA3_PMINFO
+#ifdef VBE
+#undef CIRRUS_VESA3_PMINFO
+#endif
+
+#define PM_BIOSMEM_CURRENT_MODE 0x449
+#define PM_BIOSMEM_CRTC_ADDRESS 0x463
+#define PM_BIOSMEM_VBE_MODE 0x4BA
+
+typedef struct
+{
+ /* + 0 */
+ unsigned short mode;
+ unsigned short width;
+ unsigned short height;
+ unsigned short depth;
+ /* + 8 */
+ unsigned short hidden_dac; /* 0x3c6 */
+ unsigned short *seq; /* 0x3c4 */
+ unsigned short *graph; /* 0x3ce */
+ unsigned short *crtc; /* 0x3d4 */
+ /* +16 */
+ unsigned char bitsperpixel;
+ unsigned char vesacolortype;
+ unsigned char vesaredmask;
+ unsigned char vesaredpos;
+ unsigned char vesagreenmask;
+ unsigned char vesagreenpos;
+ unsigned char vesabluemask;
+ unsigned char vesabluepos;
+ /* +24 */
+ unsigned char vesareservedmask;
+ unsigned char vesareservedpos;
+} cirrus_mode_t;
+#define CIRRUS_MODE_SIZE 26
+
+
+/* For VESA BIOS 3.0 */
+#define CIRRUS_PM16INFO_SIZE 20
+
+/* VGA */
+unsigned short cseq_vga[] = {0x0007,0xffff};
+unsigned short cgraph_vga[] = {0x0009,0x000a,0x000b,0xffff};
+unsigned short ccrtc_vga[] = {0x001a,0x001b,0x001d,0xffff};
+
+/* extensions */
+unsigned short cgraph_svgacolor[] = {
+0x0000,0x0001,0x0002,0x0003,0x0004,0x4005,0x0506,0x0f07,0xff08,
+0x0009,0x000a,0x000b,
+0xffff
+};
+/* 640x480x8 */
+unsigned short cseq_640x480x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x580b,0x580c,0x580d,0x580e,
+0x0412,0x0013,0x2017,
+0x331b,0x331c,0x331d,0x331e,
+0xffff
+};
+unsigned short ccrtc_640x480x8[] = {
+0x2c11,
+0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
+0x4009,0x000c,0x000d,
+0xea10,0xdf12,0x5013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 640x480x16 */
+unsigned short cseq_640x480x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x580b,0x580c,0x580d,0x580e,
+0x0412,0x0013,0x2017,
+0x331b,0x331c,0x331d,0x331e,
+0xffff
+};
+unsigned short ccrtc_640x480x16[] = {
+0x2c11,
+0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
+0x4009,0x000c,0x000d,
+0xea10,0xdf12,0xa013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 640x480x24 */
+unsigned short cseq_640x480x24[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
+0x580b,0x580c,0x580d,0x580e,
+0x0412,0x0013,0x2017,
+0x331b,0x331c,0x331d,0x331e,
+0xffff
+};
+unsigned short ccrtc_640x480x24[] = {
+0x2c11,
+0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
+0x4009,0x000c,0x000d,
+0xea10,0xdf12,0x0013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 800x600x8 */
+unsigned short cseq_800x600x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x230b,0x230c,0x230d,0x230e,
+0x0412,0x0013,0x2017,
+0x141b,0x141c,0x141d,0x141e,
+0xffff
+};
+unsigned short ccrtc_800x600x8[] = {
+0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
+0x6009,0x000c,0x000d,
+0x7d10,0x5712,0x6413,0x4014,0x5715,0x9816,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 800x600x16 */
+unsigned short cseq_800x600x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x230b,0x230c,0x230d,0x230e,
+0x0412,0x0013,0x2017,
+0x141b,0x141c,0x141d,0x141e,
+0xffff
+};
+unsigned short ccrtc_800x600x16[] = {
+0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
+0x6009,0x000c,0x000d,
+0x7d10,0x5712,0xc813,0x4014,0x5715,0x9816,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 800x600x24 */
+unsigned short cseq_800x600x24[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
+0x230b,0x230c,0x230d,0x230e,
+0x0412,0x0013,0x2017,
+0x141b,0x141c,0x141d,0x141e,
+0xffff
+};
+unsigned short ccrtc_800x600x24[] = {
+0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
+0x6009,0x000c,0x000d,
+0x7d10,0x5712,0x2c13,0x4014,0x5715,0x9816,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 1024x768x8 */
+unsigned short cseq_1024x768x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1024x768x8[] = {
+0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 1024x768x16 */
+unsigned short cseq_1024x768x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1024x768x16[] = {
+0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x0013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 1024x768x24 */
+unsigned short cseq_1024x768x24[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1024x768x24[] = {
+0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 1280x1024x8 */
+unsigned short cseq_1280x1024x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1280x1024x8[] = {
+0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 1280x1024x16 */
+unsigned short cseq_1280x1024x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1280x1024x16[] = {
+0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x4013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+
+/* 1600x1200x8 */
+unsigned short cseq_1600x1200x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1600x1200x8[] = {
+0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+
+cirrus_mode_t cirrus_modes[] =
+{
+ {0x5f,640,480,8,0x00,
+ cseq_640x480x8,cgraph_svgacolor,ccrtc_640x480x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x64,640,480,16,0xe1,
+ cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16,
+ 6,5,11,6,5,5,0,0,0},
+ {0x66,640,480,15,0xf0,
+ cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16,
+ 6,5,10,5,5,5,0,1,15},
+ {0x71,640,480,24,0xe5,
+ cseq_640x480x24,cgraph_svgacolor,ccrtc_640x480x24,24,
+ 6,8,16,8,8,8,0,0,0},
+
+ {0x5c,800,600,8,0x00,
+ cseq_800x600x8,cgraph_svgacolor,ccrtc_800x600x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x65,800,600,16,0xe1,
+ cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16,
+ 6,5,11,6,5,5,0,0,0},
+ {0x67,800,600,15,0xf0,
+ cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16,
+ 6,5,10,5,5,5,0,1,15},
+
+ {0x60,1024,768,8,0x00,
+ cseq_1024x768x8,cgraph_svgacolor,ccrtc_1024x768x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x74,1024,768,16,0xe1,
+ cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16,
+ 6,5,11,6,5,5,0,0,0},
+ {0x68,1024,768,15,0xf0,
+ cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16,
+ 6,5,10,5,5,5,0,1,15},
+
+ {0x78,800,600,24,0xe5,
+ cseq_800x600x24,cgraph_svgacolor,ccrtc_800x600x24,24,
+ 6,8,16,8,8,8,0,0,0},
+ {0x79,1024,768,24,0xe5,
+ cseq_1024x768x24,cgraph_svgacolor,ccrtc_1024x768x24,24,
+ 6,8,16,8,8,8,0,0,0},
+
+ {0x6d,1280,1024,8,0x00,
+ cseq_1280x1024x8,cgraph_svgacolor,ccrtc_1280x1024x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x69,1280,1024,15,0xf0,
+ cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16,
+ 6,5,10,5,5,5,0,1,15},
+ {0x75,1280,1024,16,0xe1,
+ cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16,
+ 6,5,11,6,5,5,0,0,0},
+
+ {0x7b,1600,1200,8,0x00,
+ cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8,8,
+ 4,0,0,0,0,0,0,0,0},
+
+ {0xfe,0,0,0,0,cseq_vga,cgraph_vga,ccrtc_vga,0,
+ 0xff,0,0,0,0,0,0,0,0},
+ {0xff,0,0,0,0,0,0,0,0,
+ 0xff,0,0,0,0,0,0,0,0},
+};
+
+unsigned char cirrus_id_table[] = {
+ // 5430
+ 0xA0, 0x32,
+ // 5446
+ 0xB8, 0x39,
+
+ 0xff, 0xff
+};
+
+
+unsigned short cirrus_vesa_modelist[] = {
+// 640x480x8
+ 0x101, 0x5f,
+// 640x480x15
+ 0x110, 0x66,
+// 640x480x16
+ 0x111, 0x64,
+// 640x480x24
+ 0x112, 0x71,
+// 800x600x8
+ 0x103, 0x5c,
+// 800x600x15
+ 0x113, 0x67,
+// 800x600x16
+ 0x114, 0x65,
+// 800x600x24
+ 0x115, 0x78,
+// 1024x768x8
+ 0x105, 0x60,
+// 1024x768x15
+ 0x116, 0x68,
+// 1024x768x16
+ 0x117, 0x74,
+// 1024x768x24
+ 0x118, 0x79,
+// 1280x1024x8
+ 0x107, 0x6d,
+// 1280x1024x15
+ 0x119, 0x69,
+// 1280x1024x16
+ 0x11a, 0x75,
+// invalid
+ 0xffff,0xffff
+};
+
+
+ASM_START
+
+cirrus_installed:
+.ascii "cirrus-compatible VGA is detected"
+.byte 0x0d,0x0a
+.byte 0x0d,0x0a,0x00
+
+cirrus_not_installed:
+.ascii "cirrus-compatible VGA is not detected"
+.byte 0x0d,0x0a
+.byte 0x0d,0x0a,0x00
+
+cirrus_vesa_vendorname:
+cirrus_vesa_productname:
+cirrus_vesa_oemname:
+.ascii "VGABIOS Cirrus extension"
+.byte 0
+cirrus_vesa_productrevision:
+.ascii "1.0"
+.byte 0
+
+cirrus_init:
+ call cirrus_check
+ jnz no_cirrus
+ SET_INT_VECTOR(0x10, #0xC000, #cirrus_int10_handler)
+ mov al, #0x0f ; memory setup
+ mov dx, #0x3C4
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x18
+ mov ah, al
+ mov al, #0x0a
+ dec dx
+ out dx, ax
+ mov ax, #0x0007 ; set vga mode
+ out dx, ax
+ mov ax, #0x0431 ; reset bitblt
+ mov dx, #0x3CE
+ out dx, ax
+ mov ax, #0x0031
+ out dx, ax
+no_cirrus:
+ ret
+
+cirrus_display_info:
+ push ds
+ push si
+ push cs
+ pop ds
+ call cirrus_check
+ mov si, #cirrus_not_installed
+ jnz cirrus_msgnotinstalled
+ mov si, #cirrus_installed
+
+cirrus_msgnotinstalled:
+ call _display_string
+ pop si
+ pop ds
+ ret
+
+cirrus_check:
+ push ax
+ push dx
+ mov ax, #0x9206
+ mov dx, #0x3C4
+ out dx, ax
+ inc dx
+ in al, dx
+ cmp al, #0x12
+ pop dx
+ pop ax
+ ret
+
+
+cirrus_int10_handler:
+ pushf
+ push bp
+ cmp ah, #0x00 ;; set video mode
+ jz cirrus_set_video_mode
+ cmp ah, #0x12 ;; cirrus extension
+ jz cirrus_extbios
+ cmp ah, #0x4F ;; VESA extension
+ jz cirrus_vesa
+
+cirrus_unhandled:
+ pop bp
+ popf
+ jmp vgabios_int10_handler
+
+cirrus_return:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ pop bp
+ popf
+ iret
+
+cirrus_set_video_mode:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ push si
+ push ax
+ push bx
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov si, [cirrus_vesa_sel0000_data]
+#else
+ xor si, si
+#endif
+ mov ds, si
+ xor bx, bx
+ mov [PM_BIOSMEM_VBE_MODE], bx
+ pop ds
+ pop bx
+ call cirrus_get_modeentry
+ jnc cirrus_set_video_mode_extended
+ mov al, #0xfe
+ call cirrus_get_modeentry_nomask
+ call cirrus_switch_mode
+ pop ax
+ pop si
+ jmp cirrus_unhandled
+
+cirrus_extbios:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ cmp bl, #0x80
+ jb cirrus_unhandled
+ cmp bl, #0xAF
+ ja cirrus_unhandled
+ push bx
+ and bx, #0x7F
+ shl bx, 1
+ db 0x2e ;; cs:
+ mov bp, cirrus_extbios_handlers[bx]
+ pop bx
+ push #cirrus_return
+ push bp
+ ret
+
+cirrus_vesa:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ cmp al, #0x10
+ ja cirrus_vesa_not_handled
+ push bx
+ xor bx, bx
+ mov bl, al
+ shl bx, 1
+ db 0x2e ;; cs:
+ mov bp, cirrus_vesa_handlers[bx]
+ pop bx
+ push #cirrus_return
+ push bp
+ ret
+
+cirrus_vesa_not_handled:
+ mov ax, #0x014F ;; not implemented
+ jmp cirrus_return
+
+#ifdef CIRRUS_DEBUG
+cirrus_debug_dump:
+ push es
+ push ds
+ pusha
+ push cs
+ pop ds
+ call _cirrus_debugmsg
+ popa
+ pop ds
+ pop es
+ ret
+#endif
+
+cirrus_set_video_mode_extended:
+ call cirrus_switch_mode
+ pop ax ;; mode
+ test al, #0x80
+ jnz cirrus_set_video_mode_extended_1
+ push ax
+ mov ax, #0xffff ; set to 0xff to keep win 2K happy
+ call cirrus_clear_vram
+ pop ax
+cirrus_set_video_mode_extended_1:
+ and al, #0x7f
+
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov si, [cirrus_vesa_sel0000_data]
+#else
+ xor si, si
+#endif
+ mov ds, si
+ mov [PM_BIOSMEM_CURRENT_MODE], al
+ pop ds
+
+ mov al, #0x20
+
+ pop si
+ jmp cirrus_return
+
+cirrus_vesa_pmbios_init:
+ retf
+cirrus_vesa_pmbios_entry:
+ pushf
+ push bp
+ cmp ah, #0x4F
+ jnz cirrus_vesa_pmbios_unimplemented
+ cmp al, #0x0F
+ ja cirrus_vesa_pmbios_unimplemented
+ push bx
+ xor bx, bx
+ mov bl, al
+ shl bx, 1
+ db 0x2e ;; cs:
+ mov bp, cirrus_vesa_handlers[bx]
+ pop bx
+ push #cirrus_vesa_pmbios_return
+ push bp
+ ret
+cirrus_vesa_pmbios_unimplemented:
+ mov ax, #0x014F
+cirrus_vesa_pmbios_return:
+ pop bp
+ popf
+ retf
+
+; in si:mode table
+cirrus_switch_mode:
+ push ds
+ push bx
+ push dx
+ push cs
+ pop ds
+
+ mov bx, [si+10] ;; seq
+ mov dx, #0x3c4
+ mov ax, #0x1206
+ out dx, ax ;; Unlock cirrus special
+ call cirrus_switch_mode_setregs
+
+ mov bx, [si+12] ;; graph
+ mov dx, #0x3ce
+ call cirrus_switch_mode_setregs
+
+ mov bx, [si+14] ;; crtc
+ call cirrus_get_crtc
+ call cirrus_switch_mode_setregs
+
+ mov dx, #0x3c6
+ mov al, #0x00
+ out dx, al
+ in al, dx
+ in al, dx
+ in al, dx
+ in al, dx
+ mov al, [si+8] ;; hidden dac
+ out dx, al
+ mov al, #0xff
+ out dx, al
+
+ mov al, #0x00
+ mov bl, [si+17] ;; memory model
+ or bl, bl
+ jz is_text_mode
+ mov al, #0x01
+ cmp bl, #0x03
+ jnz is_text_mode
+ or al, #0x40
+is_text_mode:
+ mov bl, #0x10
+ call biosfn_get_single_palette_reg
+ and bh, #0xfe
+ or bh, al
+ call biosfn_set_single_palette_reg
+
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+cirrus_enable_16k_granularity:
+ push ax
+ push dx
+ mov dx, #0x3ce
+ mov al, #0x0b
+ out dx, al
+ inc dx
+ in al, dx
+ or al, #0x20 ;; enable 16k
+ out dx, al
+ pop dx
+ pop ax
+ ret
+
+cirrus_switch_mode_setregs:
+csms_1:
+ mov ax, [bx]
+ cmp ax, #0xffff
+ jz csms_2
+ out dx, ax
+ add bx, #0x2
+ jmp csms_1
+csms_2:
+ ret
+
+cirrus_extbios_80h:
+ push dx
+ call cirrus_get_crtc
+ mov al, #0x27
+ out dx, al
+ inc dx
+ in al, dx
+ mov bx, #_cirrus_id_table
+c80h_1:
+ db 0x2e ;; cs:
+ mov ah, [bx]
+ cmp ah, al
+ jz c80h_2
+ cmp ah, #0xff
+ jz c80h_2
+ inc bx
+ inc bx
+ jmp c80h_1
+c80h_2:
+ db 0x2e ;; cs:
+ mov al, 0x1[bx]
+ pop dx
+ mov ah, #0x00
+ xor bx, bx
+ ret
+
+cirrus_extbios_81h:
+ mov ax, #0x100 ;; XXX
+ ret
+cirrus_extbios_82h:
+ push dx
+ call cirrus_get_crtc
+ xor ax, ax
+ mov al, #0x27
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x03
+ mov ah, #0xAF
+ pop dx
+ ret
+
+cirrus_extbios_85h:
+ push cx
+ push dx
+ mov dx, #0x3C4
+ mov al, #0x0f ;; get DRAM band width
+ out dx, al
+ inc dx
+ in al, dx
+ ;; al = 4 << bandwidth
+ mov cl, al
+ shr cl, #0x03
+ and cl, #0x03
+ cmp cl, #0x03
+ je c85h2
+ mov al, #0x04
+ shl al, cl
+ jmp c85h3
+c85h2:
+;; 4MB or 2MB
+ and al, #0x80
+ mov al, #0x20 ;; 2 MB
+ je c85h3
+ mov al, #0x40 ;; 4 MB
+c85h3:
+ pop dx
+ pop cx
+ ret
+
+cirrus_extbios_9Ah:
+ mov ax, #0x4060
+ mov cx, #0x1132
+ ret
+
+cirrus_extbios_A0h:
+ call cirrus_get_modeentry
+ mov ah, #0x01
+ sbb ah, #0x00
+ mov bx, cirrus_extbios_A0h_callback
+ mov si, #0xffff
+ mov di, bx
+ mov ds, bx
+ mov es, bx
+ ret
+
+cirrus_extbios_A0h_callback:
+ ;; fatal: not implemented yet
+ cli
+ hlt
+ retf
+
+cirrus_extbios_A1h:
+ mov bx, #0x0E00 ;; IBM 8512/8513, color
+ ret
+
+cirrus_extbios_A2h:
+ mov al, #0x07 ;; HSync 31.5 - 64.0 kHz
+ ret
+
+cirrus_extbios_AEh:
+ mov al, #0x01 ;; High Refresh 75Hz
+ ret
+
+cirrus_extbios_unimplemented:
+ ret
+
+cirrus_vesa_00h:
+ push ds
+ push si
+ mov bp, di
+ push es
+ pop ds
+ cld
+ mov ax, [di]
+ cmp ax, #0x4256 ;; VB
+ jnz cv00_1
+ mov ax, [di+2]
+ cmp ax, #0x3245 ;; E2
+ jnz cv00_1
+ ;; VBE2
+ lea di, 0x14[bp]
+ mov ax, #0x0100 ;; soft ver.
+ stosw
+ mov ax, # cirrus_vesa_vendorname
+ stosw
+ mov ax, cs
+ stosw
+ mov ax, # cirrus_vesa_productname
+ stosw
+ mov ax, cs
+ stosw
+ mov ax, # cirrus_vesa_productrevision
+ stosw
+ mov ax, cs
+ stosw
+cv00_1:
+ mov di, bp
+ mov ax, #0x4556 ;; VE
+ stosw
+ mov ax, #0x4153 ;; SA
+ stosw
+ mov ax, #0x0200 ;; v2.00
+ stosw
+ mov ax, # cirrus_vesa_oemname
+ stosw
+ mov ax, cs
+ stosw
+ xor ax, ax ;; caps
+ stosw
+ stosw
+ lea ax, 0x40[bp]
+ stosw
+ mov ax, es
+ stosw
+ call cirrus_extbios_85h ;; vram in 64k
+ mov ah, #0x00
+ stosw
+
+ push cs
+ pop ds
+ lea di, 0x40[bp]
+ mov si, #_cirrus_vesa_modelist
+cv00_2:
+ lodsw
+ stosw
+ add si, #2
+ cmp ax, #0xffff
+ jnz cv00_2
+
+ mov ax, #0x004F
+ mov di, bp
+ pop si
+ pop ds
+ ret
+
+cirrus_vesa_01h:
+ mov ax, cx
+ and ax, #0x3fff
+ call cirrus_vesamode_to_mode
+ cmp ax, #0xffff
+ jnz cirrus_vesa_01h_1
+ jmp cirrus_vesa_unimplemented
+cirrus_vesa_01h_1:
+ push ds
+ push si
+ push cx
+ push dx
+ push bx
+ mov bp, di
+ cld
+ push cs
+ pop ds
+ call cirrus_get_modeentry_nomask
+
+ push di
+ xor ax, ax
+ mov cx, #0x80
+ rep
+ stosw ;; clear buffer
+ pop di
+
+ mov ax, #0x003b ;; mode
+ stosw
+ mov ax, #0x0007 ;; attr
+ stosw
+ mov ax, #0x0010 ;; granularity =16K
+ stosw
+ mov ax, #0x0040 ;; size =64K
+ stosw
+ mov ax, #0xA000 ;; segment A
+ stosw
+ xor ax, ax ;; no segment B
+ stosw
+ mov ax, #cirrus_vesa_05h_farentry
+ stosw
+ mov ax, cs
+ stosw
+ call cirrus_get_line_offset_entry
+ stosw ;; bytes per scan line
+ mov ax, [si+2] ;; width
+ stosw
+ mov ax, [si+4] ;; height
+ stosw
+ mov ax, #0x08
+ stosb
+ mov ax, #0x10
+ stosb
+ mov al, #1 ;; count of planes
+ stosb
+ mov al, [si+6] ;; bpp
+ stosb
+ mov al, #0x1 ;; XXX number of banks
+ stosb
+ mov al, [si+17]
+ stosb ;; memory model
+ mov al, #0x0 ;; XXX size of bank in K
+ stosb
+ call cirrus_get_line_offset_entry
+ mov bx, [si+4]
+ mul bx ;; dx:ax=vramdisp
+ or ax, ax
+ jz cirrus_vesa_01h_3
+ inc dx
+cirrus_vesa_01h_3:
+ call cirrus_extbios_85h ;; al=vram in 64k
+ mov ah, #0x00
+ mov cx, dx
+ xor dx, dx
+ div cx
+ dec ax
+ stosb ;; number of image pages = vramtotal/vramdisp-1
+ mov al, #0x00
+ stosb
+
+ ;; v1.2+ stuffs
+ push si
+ add si, #18
+ movsw
+ movsw
+ movsw
+ movsw
+ pop si
+
+ mov ah, [si+16]
+ mov al, #0x0
+ sub ah, #9
+ rcl al, #1 ; bit 0=palette flag
+ stosb ;; direct screen mode info
+
+ ;; v2.0+ stuffs
+ ;; 32-bit LFB address
+ xor ax, ax
+ stosw
+ call cirrus_get_lfb_addr
+ stosw
+ or ax, ax
+ jz cirrus_vesa_01h_4
+ push di
+ mov di, bp
+ db 0x26 ;; es:
+ mov ax, [di]
+ or ax, #0x0080 ;; mode bit 7:LFB
+ stosw
+ pop di
+cirrus_vesa_01h_4:
+
+ xor ax, ax
+ stosw ; reserved
+ stosw ; reserved
+ stosw ; reserved
+
+ mov ax, #0x004F
+ mov di, bp
+ pop bx
+ pop dx
+ pop cx
+ pop si
+ pop ds
+
+ test cx, #0x4000 ;; LFB flag
+ jz cirrus_vesa_01h_5
+ push cx
+ db 0x26 ;; es:
+ mov cx, [di]
+ cmp cx, #0x0080 ;; is LFB supported?
+ jnz cirrus_vesa_01h_6
+ mov ax, #0x014F ;; error - no LFB
+cirrus_vesa_01h_6:
+ pop cx
+cirrus_vesa_01h_5:
+ ret
+
+cirrus_vesa_02h:
+ ;; XXX support CRTC registers
+ test bx, #0x3e00
+ jnz cirrus_vesa_02h_2 ;; unknown flags
+ mov ax, bx
+ and ax, #0x1ff ;; bit 8-0 mode
+ cmp ax, #0x100 ;; legacy VGA mode
+ jb cirrus_vesa_02h_legacy
+ call cirrus_vesamode_to_mode
+ cmp ax, #0xffff
+ jnz cirrus_vesa_02h_1
+cirrus_vesa_02h_2:
+ jmp cirrus_vesa_unimplemented
+cirrus_vesa_02h_legacy:
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ cmp byte ptr [cirrus_vesa_is_protected_mode], #0
+ jnz cirrus_vesa_02h_2
+#endif // CIRRUS_VESA3_PMINFO
+ int #0x10
+ mov ax, #0x004F
+ ret
+cirrus_vesa_02h_1:
+ push si
+ push ax
+ call cirrus_get_modeentry_nomask
+ call cirrus_switch_mode
+ test bx, #0x4000 ;; LFB
+ jnz cirrus_vesa_02h_3
+ call cirrus_enable_16k_granularity
+cirrus_vesa_02h_3:
+ test bx, #0x8000 ;; no clear
+ jnz cirrus_vesa_02h_4
+ push ax
+ xor ax,ax
+ call cirrus_clear_vram
+ pop ax
+cirrus_vesa_02h_4:
+ pop ax
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov si, [cirrus_vesa_sel0000_data]
+#else
+ xor si, si
+#endif
+ mov ds, si
+ mov [PM_BIOSMEM_CURRENT_MODE], al
+ mov [PM_BIOSMEM_VBE_MODE], bx
+ pop ds
+ pop si
+ mov ax, #0x004F
+ ret
+
+cirrus_vesa_03h:
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov ax, [cirrus_vesa_sel0000_data]
+#else
+ xor ax, ax
+#endif
+ mov ds, ax
+ mov bx, # PM_BIOSMEM_VBE_MODE
+ mov ax, [bx]
+ mov bx, ax
+ test bx, bx
+ jnz cirrus_vesa_03h_1
+ mov bx, # PM_BIOSMEM_CURRENT_MODE
+ mov al, [bx]
+ mov bl, al
+ xor bh, bh
+cirrus_vesa_03h_1:
+ mov ax, #0x004f
+ pop ds
+ ret
+
+cirrus_vesa_05h_farentry:
+ call cirrus_vesa_05h
+ retf
+
+cirrus_vesa_05h:
+ cmp bl, #0x01
+ ja cirrus_vesa_05h_1
+ cmp bh, #0x00
+ jz cirrus_vesa_05h_setmempage
+ cmp bh, #0x01
+ jz cirrus_vesa_05h_getmempage
+cirrus_vesa_05h_1:
+ jmp cirrus_vesa_unimplemented
+cirrus_vesa_05h_setmempage:
+ or dh, dh ; address must be < 0x100
+ jnz cirrus_vesa_05h_1
+ push dx
+ mov al, bl ;; bl=bank number
+ add al, #0x09
+ mov ah, dl ;; dx=window address in granularity
+ mov dx, #0x3ce
+ out dx, ax
+ pop dx
+ mov ax, #0x004F
+ ret
+cirrus_vesa_05h_getmempage:
+ mov al, bl ;; bl=bank number
+ add al, #0x09
+ mov dx, #0x3ce
+ out dx, al
+ inc dx
+ in al, dx
+ xor dx, dx
+ mov dl, al ;; dx=window address in granularity
+ mov ax, #0x004F
+ ret
+
+cirrus_vesa_06h:
+ mov ax, cx
+ cmp bl, #0x01
+ je cirrus_vesa_06h_3
+ cmp bl, #0x02
+ je cirrus_vesa_06h_2
+ jb cirrus_vesa_06h_1
+ mov ax, #0x0100
+ ret
+cirrus_vesa_06h_1:
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ mov ax, cx
+ mul bx
+cirrus_vesa_06h_2:
+ call cirrus_set_line_offset
+cirrus_vesa_06h_3:
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ xor dx, dx
+ call cirrus_get_line_offset
+ push ax
+ div bx
+ mov cx, ax
+ pop bx
+ call cirrus_extbios_85h ;; al=vram in 64k
+ xor dx, dx
+ mov dl, al
+ xor ax, ax
+ div bx
+ mov dx, ax
+ mov ax, #0x004f
+ ret
+
+cirrus_vesa_07h:
+ cmp bl, #0x80
+ je cirrus_vesa_07h_1
+ cmp bl, #0x01
+ je cirrus_vesa_07h_2
+ jb cirrus_vesa_07h_1
+ mov ax, #0x0100
+ ret
+cirrus_vesa_07h_1:
+ push dx
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ mov ax, cx
+ mul bx
+ pop bx
+ push ax
+ call cirrus_get_line_offset
+ mul bx
+ pop bx
+ add ax, bx
+ jnc cirrus_vesa_07h_3
+ inc dx
+cirrus_vesa_07h_3:
+ push dx
+ and dx, #0x0003
+ mov bx, #0x04
+ div bx
+ pop dx
+ shr dx, #2
+ call cirrus_set_start_addr
+ mov ax, #0x004f
+ ret
+cirrus_vesa_07h_2:
+ call cirrus_get_start_addr
+ shl dx, #2
+ push dx
+ mov bx, #0x04
+ mul bx
+ pop bx
+ or dx, bx
+ push ax
+ call cirrus_get_line_offset
+ mov bx, ax
+ pop ax
+ div bx
+ push ax
+ push dx
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ pop ax
+ xor dx, dx
+ div bx
+ mov cx, ax
+ pop dx
+ mov ax, #0x004f
+ ret
+
+cirrus_vesa_10h:
+ cmp bl, #0x00
+ jne cirrus_vesa_10h_01
+ mov bx, #0x0f30
+ mov ax, #0x004f
+ ret
+cirrus_vesa_10h_01:
+ cmp bl, #0x01
+ jne cirrus_vesa_10h_02
+ push dx
+ push ds
+ mov dx, #0x40
+ mov ds, dx
+ mov [0xb9], bh
+ pop ds
+ pop dx
+ mov ax, #0x004f
+ ret
+cirrus_vesa_10h_02:
+ cmp bl, #0x02
+ jne cirrus_vesa_unimplemented
+ push dx
+ push ds
+ mov dx, #0x40
+ mov ds, dx
+ mov bh, [0xb9]
+ pop ds
+ pop dx
+ mov ax, #0x004f
+ ret
+
+cirrus_vesa_unimplemented:
+ mov ax, #0x014F ;; not implemented
+ ret
+
+
+;; in ax:vesamode, out ax:cirrusmode
+cirrus_vesamode_to_mode:
+ push ds
+ push cx
+ push si
+ push cs
+ pop ds
+ mov cx, #0xffff
+ mov si, #_cirrus_vesa_modelist
+cvtm_1:
+ cmp [si],ax
+ jz cvtm_2
+ cmp [si],cx
+ jz cvtm_2
+ add si, #4
+ jmp cvtm_1
+cvtm_2:
+ mov ax,[si+2]
+ pop si
+ pop cx
+ pop ds
+ ret
+
+ ; cirrus_get_crtc
+ ;; NOTE - may be called in protected mode
+cirrus_get_crtc:
+ push ds
+ push ax
+ mov dx, #0x3cc
+ in al, dx
+ and al, #0x01
+ shl al, #5
+ mov dx, #0x3b4
+ add dl, al
+ pop ax
+ pop ds
+ ret
+
+;; in - al:mode, out - cflag:result, si:table, ax:destroyed
+cirrus_get_modeentry:
+ and al, #0x7f
+cirrus_get_modeentry_nomask:
+ mov si, #_cirrus_modes
+cgm_1:
+ db 0x2e ;; cs:
+ mov ah, [si]
+ cmp al, ah
+ jz cgm_2
+ cmp ah, #0xff
+ jz cgm_4
+ add si, # CIRRUS_MODE_SIZE
+ jmp cgm_1
+cgm_4:
+ xor si, si
+ stc ;; video mode is not supported
+ jmp cgm_3
+cgm_2:
+ clc ;; video mode is supported
+cgm_3:
+ ret
+
+ ; get LFB address
+ ; out - ax:LFB address (high 16 bit)
+ ;; NOTE - may be called in protected mode
+cirrus_get_lfb_addr:
+ push cx
+ push dx
+ push eax
+ xor cx, cx
+ mov dl, #0x00
+ call cirrus_pci_read
+ cmp ax, #0xffff
+ jz cirrus_get_lfb_addr_5
+ cirrus_get_lfb_addr_3:
+ mov dl, #0x00
+ call cirrus_pci_read
+ cmp ax, #0x1013 ;; cirrus
+ jz cirrus_get_lfb_addr_4
+ add cx, #0x8
+ cmp cx, #0x200 ;; search bus #0 and #1
+ jb cirrus_get_lfb_addr_3
+ cirrus_get_lfb_addr_5:
+ xor dx, dx ;; no LFB
+ jmp cirrus_get_lfb_addr_6
+ cirrus_get_lfb_addr_4:
+ mov dl, #0x10 ;; I/O space #0
+ call cirrus_pci_read
+ test ax, #0xfff1
+ jnz cirrus_get_lfb_addr_5
+ shr eax, #16
+ mov dx, ax ;; LFB address
+ cirrus_get_lfb_addr_6:
+ pop eax
+ mov ax, dx
+ pop dx
+ pop cx
+ ret
+
+cirrus_pci_read:
+ mov eax, #0x00800000
+ mov ax, cx
+ shl eax, #8
+ mov al, dl
+ mov dx, #0xcf8
+ out dx, eax
+ add dl, #4
+ in eax, dx
+ ret
+
+;; out - al:bytes per pixel
+cirrus_get_bpp_bytes:
+ push dx
+ mov dx, #0x03c4
+ mov al, #0x07
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x0e
+ cmp al, #0x06
+ jne cirrus_get_bpp_bytes_1
+ and al, #0x02
+cirrus_get_bpp_bytes_1:
+ shr al, #1
+ cmp al, #0x04
+ je cirrus_get_bpp_bytes_2
+ inc al
+cirrus_get_bpp_bytes_2:
+ pop dx
+ ret
+
+;; in - ax: new line offset
+cirrus_set_line_offset:
+ shr ax, #3
+ push ax
+ call cirrus_get_crtc
+ mov al, #0x13
+ out dx, al
+ inc dx
+ pop ax
+ out dx, al
+ dec dx
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ shl ah, #4
+ in al, dx
+ and al, #ef
+ or al, ah
+ out dx, al
+ ret
+
+;; out - ax: active line offset
+cirrus_get_line_offset:
+ push dx
+ push bx
+ call cirrus_get_crtc
+ mov al, #0x13
+ out dx, al
+ inc dx
+ in al, dx
+ mov bl, al
+ dec dx
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ in al, dx
+ mov ah, al
+ shr ah, #4
+ and ah, #0x01
+ mov al, bl
+ shl ax, #3
+ pop bx
+ pop dx
+ ret
+
+;; in - si: table
+;; out - ax: line offset for mode
+cirrus_get_line_offset_entry:
+ push bx
+ mov bx, [si+14] ;; crtc table
+ push bx
+offset_loop1:
+ mov ax, [bx]
+ cmp al, #0x13
+ je offset_found1
+ inc bx
+ inc bx
+ jnz offset_loop1
+offset_found1:
+ xor al, al
+ shr ax, #5
+ pop bx
+ push ax
+offset_loop2:
+ mov ax, [bx]
+ cmp al, #0x1b
+ je offset_found2
+ inc bx
+ inc bx
+ jnz offset_loop2
+offset_found2:
+ pop bx
+ and ax, #0x1000
+ shr ax, #1
+ or ax, bx
+ pop bx
+ ret
+
+;; in - new address in DX:AX
+cirrus_set_start_addr:
+ push bx
+ push dx
+ push ax
+ call cirrus_get_crtc
+ mov al, #0x0d
+ out dx, al
+ inc dx
+ pop ax
+ out dx, al
+ dec dx
+ mov al, #0x0c
+ out dx, al
+ inc dx
+ mov al, ah
+ out dx, al
+ dec dx
+ mov al, #0x1d
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x7f
+ pop bx
+ mov ah, bl
+ shl bl, #4
+ and bl, #0x80
+ or al, bl
+ out dx, al
+ dec dx
+ mov bl, ah
+ and ah, #0x01
+ shl bl, #1
+ and bl, #0x0c
+ or ah, bl
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0xf2
+ or al, ah
+ out dx, al
+ pop bx
+ ret
+
+;; out - current address in DX:AX
+cirrus_get_start_addr:
+ push bx
+ call cirrus_get_crtc
+ mov al, #0x0c
+ out dx, al
+ inc dx
+ in al, dx
+ mov ah, al
+ dec dx
+ mov al, #0x0d
+ out dx, al
+ inc dx
+ in al, dx
+ push ax
+ dec dx
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ in al, dx
+ dec dx
+ mov bl, al
+ and al, #0x01
+ and bl, #0x0c
+ shr bl, #1
+ or bl, al
+ mov al, #0x1d
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x80
+ shr al, #4
+ or bl, al
+ mov dl, bl
+ xor dh, dh
+ pop ax
+ pop bx
+ ret
+
+cirrus_clear_vram:
+ pusha
+ push es
+ mov si, ax
+
+ call cirrus_enable_16k_granularity
+ call cirrus_extbios_85h
+ shl al, #2
+ mov bl, al
+ xor ah,ah
+cirrus_clear_vram_1:
+ mov al, #0x09
+ mov dx, #0x3ce
+ out dx, ax
+ push ax
+ mov cx, #0xa000
+ mov es, cx
+ xor di, di
+ mov ax, si
+ mov cx, #8192
+ cld
+ rep
+ stosw
+ pop ax
+ inc ah
+ cmp ah, bl
+ jne cirrus_clear_vram_1
+
+ xor ah,ah
+ mov dx, #0x3ce
+ out dx, ax
+
+ pop es
+ popa
+ ret
+
+cirrus_extbios_handlers:
+ ;; 80h
+ dw cirrus_extbios_80h
+ dw cirrus_extbios_81h
+ dw cirrus_extbios_82h
+ dw cirrus_extbios_unimplemented
+ ;; 84h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_85h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 88h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 8Ch
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 90h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 94h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 98h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_9Ah
+ dw cirrus_extbios_unimplemented
+ ;; 9Ch
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; A0h
+ dw cirrus_extbios_A0h
+ dw cirrus_extbios_A1h
+ dw cirrus_extbios_A2h
+ dw cirrus_extbios_unimplemented
+ ;; A4h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; A8h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; ACh
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_AEh
+ dw cirrus_extbios_unimplemented
+
+cirrus_vesa_handlers:
+ ;; 00h
+ dw cirrus_vesa_00h
+ dw cirrus_vesa_01h
+ dw cirrus_vesa_02h
+ dw cirrus_vesa_03h
+ ;; 04h
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_05h
+ dw cirrus_vesa_06h
+ dw cirrus_vesa_07h
+ ;; 08h
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ ;; 0Ch
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ ;; 10h
+ dw cirrus_vesa_10h
+
+
+ASM_END
+
+#ifdef CIRRUS_VESA3_PMINFO
+ASM_START
+cirrus_vesa_pminfo:
+ /* + 0 */
+ .byte 0x50,0x4d,0x49,0x44 ;; signature[4]
+ /* + 4 */
+ dw cirrus_vesa_pmbios_entry ;; entry_bios
+ dw cirrus_vesa_pmbios_init ;; entry_init
+ /* + 8 */
+cirrus_vesa_sel0000_data:
+ dw 0x0000 ;; sel_00000
+cirrus_vesa_selA000_data:
+ dw 0xA000 ;; sel_A0000
+ /* +12 */
+cirrus_vesa_selB000_data:
+ dw 0xB000 ;; sel_B0000
+cirrus_vesa_selB800_data:
+ dw 0xB800 ;; sel_B8000
+ /* +16 */
+cirrus_vesa_selC000_data:
+ dw 0xC000 ;; sel_C0000
+cirrus_vesa_is_protected_mode:
+ ;; protected mode flag and checksum
+ dw (~((0xf2 + (cirrus_vesa_pmbios_entry >> 8) + (cirrus_vesa_pmbios_entry) \
+ + (cirrus_vesa_pmbios_init >> 8) + (cirrus_vesa_pmbios_init)) & 0xff) << 8) + 0x01
+ASM_END
+#endif // CIRRUS_VESA3_PMINFO
+
+
+#ifdef CIRRUS_DEBUG
+static void cirrus_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+ Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+ if((GET_AH()!=0x0E)&&(GET_AH()!=0x02)&&(GET_AH()!=0x09)&&(AX!=0x4F05))
+ printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX);
+}
+#endif
diff --git a/kvm/vgabios/dataseghack b/kvm/vgabios/dataseghack
new file mode 100755
index 000000000..02a2d4c52
--- /dev/null
+++ b/kvm/vgabios/dataseghack
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+awk \
+ 'BEGIN { }\
+ /^\.text/,/DATA_SEG_DEFS_HERE/ { print }\
+ END { }'\
+ $1 > temp.awk.1
+
+awk \
+ 'BEGIN { i = 0; last = "hello" }\
+ /BLOCK_STRINGS_BEGIN/,/^\.bss/ { if ( i > 1 ) { print last } last = $0; i = i + 1 }\
+ END { }'\
+ $1 > temp.awk.2
+
+awk \
+ 'BEGIN { }\
+ /DATA_SEG_DEFS_HERE/,/BLOCK_STRINGS_BEGIN/ { print }\
+ END { }'\
+ $1 > temp.awk.3
+
+cp $1 $1.orig
+cat temp.awk.1 temp.awk.2 temp.awk.3 | sed -e 's/^\.data//' -e 's/^\.bss//' -e 's/^\.text//' > $1
+/bin/rm -f temp.awk.1 temp.awk.2 temp.awk.3 $1.orig
diff --git a/kvm/vgabios/tests/lfbprof/Makefile b/kvm/vgabios/tests/lfbprof/Makefile
new file mode 100644
index 000000000..7c42e38b0
--- /dev/null
+++ b/kvm/vgabios/tests/lfbprof/Makefile
@@ -0,0 +1,5 @@
+# Very simple makefile for LFBPROF.C using Watcom C++ 10.0a with DOS4GW
+
+lfbprof.exe: lfbprof.c lfbprof.h
+ wcl386 -zq -s -d2 lfbprof.c
+
diff --git a/kvm/vgabios/tests/lfbprof/lfbprof.c b/kvm/vgabios/tests/lfbprof/lfbprof.c
new file mode 100644
index 000000000..df37452e8
--- /dev/null
+++ b/kvm/vgabios/tests/lfbprof/lfbprof.c
@@ -0,0 +1,594 @@
+/****************************************************************************
+*
+* VBE 2.0 Linear Framebuffer Profiler
+* By Kendall Bennett and Brian Hook
+*
+* Filename: LFBPROF.C
+* Language: ANSI C
+* Environment: Watcom C/C++ 10.0a with DOS4GW
+*
+* Description: Simple program to profile the speed of screen clearing
+* and full screen BitBlt operations using a VESA VBE 2.0
+* linear framebuffer from 32 bit protected mode.
+*
+* For simplicity, this program only supports 256 color
+* SuperVGA video modes that support a linear framebuffer.
+*
+*
+* 2002/02/18: Jeroen Janssen <japj at xs4all dot nl>
+* - fixed unsigned short for mode list (-1 != 0xffff otherwise)
+* - fixed LfbMapRealPointer macro mask problem (some modes were skipped)
+*
+****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <conio.h>
+#include <dos.h>
+#include "lfbprof.h"
+
+/*---------------------------- Global Variables ---------------------------*/
+
+int VESABuf_len = 1024; /* Length of VESABuf */
+int VESABuf_sel = 0; /* Selector for VESABuf */
+int VESABuf_rseg; /* Real mode segment of VESABuf */
+unsigned short modeList[50]; /* List of available VBE modes */
+float clearsPerSec; /* Number of clears per second */
+float clearsMbPerSec; /* Memory transfer for clears */
+float bitBltsPerSec; /* Number of BitBlt's per second */
+float bitBltsMbPerSec; /* Memory transfer for bitblt's */
+int xres,yres; /* Video mode resolution */
+int bytesperline; /* Bytes per scanline for mode */
+long imageSize; /* Length of the video image */
+char *LFBPtr; /* Pointer to linear framebuffer */
+
+/*------------------------- DPMI interface routines -----------------------*/
+
+void DPMI_allocRealSeg(int size,int *sel,int *r_seg)
+/****************************************************************************
+*
+* Function: DPMI_allocRealSeg
+* Parameters: size - Size of memory block to allocate
+* sel - Place to return protected mode selector
+* r_seg - Place to return real mode segment
+*
+* Description: Allocates a block of real mode memory using DPMI services.
+* This routine returns both a protected mode selector and
+* real mode segment for accessing the memory block.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 0x100; /* DPMI allocate DOS memory */
+ r.w.bx = (size + 0xF) >> 4; /* number of paragraphs */
+ int386(0x31, &r, &r);
+ if (r.w.cflag)
+ FatalError("DPMI_allocRealSeg failed!");
+ *sel = r.w.dx; /* Protected mode selector */
+ *r_seg = r.w.ax; /* Real mode segment */
+}
+
+void DPMI_freeRealSeg(unsigned sel)
+/****************************************************************************
+*
+* Function: DPMI_allocRealSeg
+* Parameters: sel - Protected mode selector of block to free
+*
+* Description: Frees a block of real mode memory.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 0x101; /* DPMI free DOS memory */
+ r.w.dx = sel; /* DX := selector from 0x100 */
+ int386(0x31, &r, &r);
+}
+
+typedef struct {
+ long edi;
+ long esi;
+ long ebp;
+ long reserved;
+ long ebx;
+ long edx;
+ long ecx;
+ long eax;
+ short flags;
+ short es,ds,fs,gs,ip,cs,sp,ss;
+ } _RMREGS;
+
+#define IN(reg) rmregs.e##reg = in->x.reg
+#define OUT(reg) out->x.reg = rmregs.e##reg
+
+int DPMI_int86(int intno, RMREGS *in, RMREGS *out)
+/****************************************************************************
+*
+* Function: DPMI_int86
+* Parameters: intno - Interrupt number to issue
+* in - Pointer to structure for input registers
+* out - Pointer to structure for output registers
+* Returns: Value returned by interrupt in AX
+*
+* Description: Issues a real mode interrupt using DPMI services.
+*
+****************************************************************************/
+{
+ _RMREGS rmregs;
+ union REGS r;
+ struct SREGS sr;
+
+ memset(&rmregs, 0, sizeof(rmregs));
+ IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
+
+ segread(&sr);
+ r.w.ax = 0x300; /* DPMI issue real interrupt */
+ r.h.bl = intno;
+ r.h.bh = 0;
+ r.w.cx = 0;
+ sr.es = sr.ds;
+ r.x.edi = (unsigned)&rmregs;
+ int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
+
+ OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
+ out->x.cflag = rmregs.flags & 0x1;
+ return out->x.ax;
+}
+
+int DPMI_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs)
+/****************************************************************************
+*
+* Function: DPMI_int86
+* Parameters: intno - Interrupt number to issue
+* in - Pointer to structure for input registers
+* out - Pointer to structure for output registers
+* sregs - Values to load into segment registers
+* Returns: Value returned by interrupt in AX
+*
+* Description: Issues a real mode interrupt using DPMI services.
+*
+****************************************************************************/
+{
+ _RMREGS rmregs;
+ union REGS r;
+ struct SREGS sr;
+
+ memset(&rmregs, 0, sizeof(rmregs));
+ IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
+ rmregs.es = sregs->es;
+ rmregs.ds = sregs->ds;
+
+ segread(&sr);
+ r.w.ax = 0x300; /* DPMI issue real interrupt */
+ r.h.bl = intno;
+ r.h.bh = 0;
+ r.w.cx = 0;
+ sr.es = sr.ds;
+ r.x.edi = (unsigned)&rmregs;
+ int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
+
+ OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
+ sregs->es = rmregs.es;
+ sregs->cs = rmregs.cs;
+ sregs->ss = rmregs.ss;
+ sregs->ds = rmregs.ds;
+ out->x.cflag = rmregs.flags & 0x1;
+ return out->x.ax;
+}
+
+int DPMI_allocSelector(void)
+/****************************************************************************
+*
+* Function: DPMI_allocSelector
+* Returns: Newly allocated protected mode selector
+*
+* Description: Allocates a new protected mode selector using DPMI
+* services. This selector has a base address and limit of 0.
+*
+****************************************************************************/
+{
+ int sel;
+ union REGS r;
+
+ r.w.ax = 0; /* DPMI allocate selector */
+ r.w.cx = 1; /* Allocate a single selector */
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_allocSelector() failed!");
+ sel = r.w.ax;
+
+ r.w.ax = 9; /* DPMI set access rights */
+ r.w.bx = sel;
+ r.w.cx = 0x8092; /* 32 bit page granular */
+ int386(0x31, &r, &r);
+ return sel;
+}
+
+long DPMI_mapPhysicalToLinear(long physAddr,long limit)
+/****************************************************************************
+*
+* Function: DPMI_mapPhysicalToLinear
+* Parameters: physAddr - Physical memory address to map
+* limit - Length-1 of physical memory region to map
+* Returns: Starting linear address for mapped memory
+*
+* Description: Maps a section of physical memory into the linear address
+* space of a process using DPMI calls. Note that this linear
+* address cannot be used directly, but must be used as the
+* base address for a selector.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 0x800; /* DPMI map physical to linear */
+ r.w.bx = physAddr >> 16;
+ r.w.cx = physAddr & 0xFFFF;
+ r.w.si = limit >> 16;
+ r.w.di = limit & 0xFFFF;
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_mapPhysicalToLinear() failed!");
+ return ((long)r.w.bx << 16) + r.w.cx;
+}
+
+void DPMI_setSelectorBase(int sel,long linAddr)
+/****************************************************************************
+*
+* Function: DPMI_setSelectorBase
+* Parameters: sel - Selector to change base address for
+* linAddr - Linear address used for new base address
+*
+* Description: Sets the base address for the specified selector.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 7; /* DPMI set selector base address */
+ r.w.bx = sel;
+ r.w.cx = linAddr >> 16;
+ r.w.dx = linAddr & 0xFFFF;
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_setSelectorBase() failed!");
+}
+
+void DPMI_setSelectorLimit(int sel,long limit)
+/****************************************************************************
+*
+* Function: DPMI_setSelectorLimit
+* Parameters: sel - Selector to change limit for
+* limit - Limit-1 for the selector
+*
+* Description: Sets the memory limit for the specified selector.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 8; /* DPMI set selector limit */
+ r.w.bx = sel;
+ r.w.cx = limit >> 16;
+ r.w.dx = limit & 0xFFFF;
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_setSelectorLimit() failed!");
+}
+
+/*-------------------------- VBE Interface routines -----------------------*/
+
+void FatalError(char *msg)
+{
+ fprintf(stderr,"%s\n", msg);
+ exit(1);
+}
+
+static void ExitVBEBuf(void)
+{
+ DPMI_freeRealSeg(VESABuf_sel);
+}
+
+void VBE_initRMBuf(void)
+/****************************************************************************
+*
+* Function: VBE_initRMBuf
+* Description: Initialises the VBE transfer buffer in real mode memory.
+* This routine is called by the VESAVBE module every time
+* it needs to use the transfer buffer, so we simply allocate
+* it once and then return.
+*
+****************************************************************************/
+{
+ if (!VESABuf_sel) {
+ DPMI_allocRealSeg(VESABuf_len, &VESABuf_sel, &VESABuf_rseg);
+ atexit(ExitVBEBuf);
+ }
+}
+
+void VBE_callESDI(RMREGS *regs, void *buffer, int size)
+/****************************************************************************
+*
+* Function: VBE_callESDI
+* Parameters: regs - Registers to load when calling VBE
+* buffer - Buffer to copy VBE info block to
+* size - Size of buffer to fill
+*
+* Description: Calls the VESA VBE and passes in a buffer for the VBE to
+* store information in, which is then copied into the users
+* buffer space. This works in protected mode as the buffer
+* passed to the VESA VBE is allocated in conventional
+* memory, and is then copied into the users memory block.
+*
+****************************************************************************/
+{
+ RMSREGS sregs;
+
+ VBE_initRMBuf();
+ sregs.es = VESABuf_rseg;
+ regs->x.di = 0;
+ _fmemcpy(MK_FP(VESABuf_sel,0),buffer,size);
+ DPMI_int86x(0x10, regs, regs, &sregs);
+ _fmemcpy(buffer,MK_FP(VESABuf_sel,0),size);
+}
+
+int VBE_detect(void)
+/****************************************************************************
+*
+* Function: VBE_detect
+* Parameters: vgaInfo - Place to store the VGA information block
+* Returns: VBE version number, or 0 if not detected.
+*
+* Description: Detects if a VESA VBE is out there and functioning
+* correctly. If we detect a VBE interface we return the
+* VGAInfoBlock returned by the VBE and the VBE version number.
+*
+****************************************************************************/
+{
+ RMREGS regs;
+ unsigned short *p1,*p2;
+ VBE_vgaInfo vgaInfo;
+
+ /* Put 'VBE2' into the signature area so that the VBE 2.0 BIOS knows
+ * that we have passed a 512 byte extended block to it, and wish
+ * the extended information to be filled in.
+ */
+ strncpy(vgaInfo.VESASignature,"VBE2",4);
+
+ /* Get the SuperVGA Information block */
+ regs.x.ax = 0x4F00;
+ VBE_callESDI(&regs, &vgaInfo, sizeof(VBE_vgaInfo));
+ if (regs.x.ax != 0x004F)
+ return 0;
+ if (strncmp(vgaInfo.VESASignature,"VESA",4) != 0)
+ return 0;
+
+ /* Now that we have detected a VBE interface, copy the list of available
+ * video modes into our local buffer. We *must* copy this mode list,
+ * since the VBE will build the mode list in the VBE_vgaInfo buffer
+ * that we have passed, so the next call to the VBE will trash the
+ * list of modes.
+ */
+ printf("videomodeptr %x\n",vgaInfo.VideoModePtr);
+ p1 = LfbMapRealPointer(vgaInfo.VideoModePtr);
+ p2 = modeList;
+ while (*p1 != -1)
+ {
+ printf("found mode %x\n",*p1);
+ *p2++ = *p1++;
+ }
+ *p2 = -1;
+ return vgaInfo.VESAVersion;
+}
+
+int VBE_getModeInfo(int mode,VBE_modeInfo *modeInfo)
+/****************************************************************************
+*
+* Function: VBE_getModeInfo
+* Parameters: mode - VBE mode to get information for
+* modeInfo - Place to store VBE mode information
+* Returns: 1 on success, 0 if function failed.
+*
+* Description: Obtains information about a specific video mode from the
+* VBE. You should use this function to find the video mode
+* you wish to set, as the new VBE 2.0 mode numbers may be
+* completely arbitrary.
+*
+****************************************************************************/
+{
+ RMREGS regs;
+
+ regs.x.ax = 0x4F01; /* Get mode information */
+ regs.x.cx = mode;
+ VBE_callESDI(&regs, modeInfo, sizeof(VBE_modeInfo));
+ if (regs.x.ax != 0x004F)
+ return 0;
+ if ((modeInfo->ModeAttributes & vbeMdAvailable) == 0)
+ return 0;
+ return 1;
+}
+
+void VBE_setVideoMode(int mode)
+/****************************************************************************
+*
+* Function: VBE_setVideoMode
+* Parameters: mode - VBE mode number to initialise
+*
+****************************************************************************/
+{
+ RMREGS regs;
+ regs.x.ax = 0x4F02;
+ regs.x.bx = mode;
+ DPMI_int86(0x10,&regs,&regs);
+}
+
+/*-------------------- Application specific routines ----------------------*/
+
+void *GetPtrToLFB(long physAddr)
+/****************************************************************************
+*
+* Function: GetPtrToLFB
+* Parameters: physAddr - Physical memory address of linear framebuffer
+* Returns: Far pointer to the linear framebuffer memory
+*
+****************************************************************************/
+{
+ int sel;
+ long linAddr,limit = (4096 * 1024) - 1;
+
+// sel = DPMI_allocSelector();
+ linAddr = DPMI_mapPhysicalToLinear(physAddr,limit);
+// DPMI_setSelectorBase(sel,linAddr);
+// DPMI_setSelectorLimit(sel,limit);
+// return MK_FP(sel,0);
+ return (void*)linAddr;
+}
+
+void AvailableModes(void)
+/****************************************************************************
+*
+* Function: AvailableModes
+*
+* Description: Display a list of available LFB mode resolutions.
+*
+****************************************************************************/
+{
+ unsigned short *p;
+ VBE_modeInfo modeInfo;
+
+ printf("Usage: LFBPROF <xres> <yres>\n\n");
+ printf("Available 256 color video modes:\n");
+ for (p = modeList; *p != -1; p++) {
+ if (VBE_getModeInfo(*p, &modeInfo)) {
+ /* Filter out only 8 bit linear framebuffer modes */
+ if ((modeInfo.ModeAttributes & vbeMdLinear) == 0)
+ continue;
+ if (modeInfo.MemoryModel != vbeMemPK
+ || modeInfo.BitsPerPixel != 8
+ || modeInfo.NumberOfPlanes != 1)
+ continue;
+ printf(" %4d x %4d %d bits per pixel\n",
+ modeInfo.XResolution, modeInfo.YResolution,
+ modeInfo.BitsPerPixel);
+ }
+ }
+ exit(1);
+}
+
+void InitGraphics(int x,int y)
+/****************************************************************************
+*
+* Function: InitGraphics
+* Parameters: x,y - Requested video mode resolution
+*
+* Description: Initialise the specified video mode. We search through
+* the list of available video modes for one that matches
+* the resolution and color depth are are looking for.
+*
+****************************************************************************/
+{
+ unsigned short *p;
+ VBE_modeInfo modeInfo;
+ printf("InitGraphics\n");
+
+ for (p = modeList; *p != -1; p++) {
+ if (VBE_getModeInfo(*p, &modeInfo)) {
+ /* Filter out only 8 bit linear framebuffer modes */
+ if ((modeInfo.ModeAttributes & vbeMdLinear) == 0)
+ continue;
+ if (modeInfo.MemoryModel != vbeMemPK
+ || modeInfo.BitsPerPixel != 8
+ || modeInfo.NumberOfPlanes != 1)
+ continue;
+ if (modeInfo.XResolution != x || modeInfo.YResolution != y)
+ continue;
+ xres = x;
+ yres = y;
+ bytesperline = modeInfo.BytesPerScanLine;
+ imageSize = bytesperline * yres;
+ VBE_setVideoMode(*p | vbeUseLFB);
+ LFBPtr = GetPtrToLFB(modeInfo.PhysBasePtr);
+ return;
+ }
+ }
+ printf("Valid video mode not found\n");
+ exit(1);
+}
+
+void EndGraphics(void)
+/****************************************************************************
+*
+* Function: EndGraphics
+*
+* Description: Restores text mode.
+*
+****************************************************************************/
+{
+ RMREGS regs;
+ printf("EndGraphics\n");
+ regs.x.ax = 0x3;
+ DPMI_int86(0x10, &regs, &regs);
+}
+
+void ProfileMode(void)
+/****************************************************************************
+*
+* Function: ProfileMode
+*
+* Description: Profiles framebuffer performance for simple screen clearing
+* and for copying from system memory to video memory (BitBlt).
+* This routine thrashes the CPU cache by cycling through
+* enough system memory buffers to invalidate the entire
+* CPU external cache before re-using the first memory buffer
+* again.
+*
+****************************************************************************/
+{
+ int i,numClears,numBlts,maxImages;
+ long startTicks,endTicks;
+ void *image[10],*dst;
+ printf("ProfileMode\n");
+
+ /* Profile screen clearing operation */
+ startTicks = LfbGetTicks();
+ numClears = 0;
+ while ((LfbGetTicks() - startTicks) < 182)
+ LfbMemset(LFBPtr,numClears++,imageSize);
+ endTicks = LfbGetTicks();
+ clearsPerSec = numClears / ((endTicks - startTicks) * 0.054925);
+ clearsMbPerSec = (clearsPerSec * imageSize) / 1048576.0;
+
+ /* Profile system memory to video memory copies */
+ maxImages = ((512 * 1024U) / imageSize) + 2;
+ for (i = 0; i < maxImages; i++) {
+ image[i] = malloc(imageSize);
+ if (image[i] == NULL)
+ FatalError("Not enough memory to profile BitBlt!");
+ memset(image[i],i+1,imageSize);
+ }
+ startTicks = LfbGetTicks();
+ numBlts = 0;
+ while ((LfbGetTicks() - startTicks) < 182)
+ LfbMemcpy(LFBPtr,image[numBlts++ % maxImages],imageSize);
+ endTicks = LfbGetTicks();
+ bitBltsPerSec = numBlts / ((endTicks - startTicks) * 0.054925);
+ bitBltsMbPerSec = (bitBltsPerSec * imageSize) / 1048576.0;
+}
+
+void main(int argc, char *argv[])
+{
+ if (VBE_detect() < 0x200)
+ FatalError("This program requires VBE 2.0; Please install UniVBE 5.1.");
+ if (argc != 3)
+ AvailableModes(); /* Display available modes */
+
+ InitGraphics(atoi(argv[1]),atoi(argv[2])); /* Start graphics */
+ ProfileMode(); /* Profile the video mode */
+ EndGraphics(); /* Restore text mode */
+
+ printf("Profiling results for %dx%d 8 bits per pixel.\n",xres,yres);
+ printf("%3.2f clears/s, %2.2f Mb/s\n", clearsPerSec, clearsMbPerSec);
+ printf("%3.2f bitBlt/s, %2.2f Mb/s\n", bitBltsPerSec, bitBltsMbPerSec);
+}
diff --git a/kvm/vgabios/tests/lfbprof/lfbprof.h b/kvm/vgabios/tests/lfbprof/lfbprof.h
new file mode 100644
index 000000000..bae0e09b1
--- /dev/null
+++ b/kvm/vgabios/tests/lfbprof/lfbprof.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+*
+* VBE 2.0 Linear Framebuffer Profiler
+* By Kendall Bennett and Brian Hook
+*
+* Filename: LFBPROF.H
+* Language: ANSI C
+* Environment: Watcom C/C++ 10.0a with DOS4GW
+*
+* Description: Header file for the LFBPROF.C progam.
+*
+****************************************************************************/
+
+#ifndef __LFBPROF_H
+#define __LFBPROF_H
+
+/*---------------------- Macros and type definitions ----------------------*/
+
+#pragma pack(1)
+
+/* SuperVGA information block */
+
+typedef struct {
+ char VESASignature[4]; /* 'VESA' 4 byte signature */
+ short VESAVersion; /* VBE version number */
+ long OemStringPtr; /* Pointer to OEM string */
+ long Capabilities; /* Capabilities of video card */
+ long VideoModePtr; /* Pointer to supported modes */
+ short TotalMemory; /* Number of 64kb memory blocks */
+
+ /* VBE 2.0 extensions */
+
+ short OemSoftwareRev; /* OEM Software revision number */
+ long OemVendorNamePtr; /* Pointer to Vendor Name string */
+ long OemProductNamePtr; /* Pointer to Product Name string */
+ long OemProductRevPtr; /* Pointer to Product Revision str */
+ char reserved[222]; /* Pad to 256 byte block size */
+ char OemDATA[256]; /* Scratch pad for OEM data */
+ } VBE_vgaInfo;
+
+/* SuperVGA mode information block */
+
+typedef struct {
+ short ModeAttributes; /* Mode attributes */
+ char WinAAttributes; /* Window A attributes */
+ char WinBAttributes; /* Window B attributes */
+ short WinGranularity; /* Window granularity in k */
+ short WinSize; /* Window size in k */
+ short WinASegment; /* Window A segment */
+ short WinBSegment; /* Window B segment */
+ long WinFuncPtr; /* Pointer to window function */
+ short BytesPerScanLine; /* Bytes per scanline */
+ short XResolution; /* Horizontal resolution */
+ short YResolution; /* Vertical resolution */
+ char XCharSize; /* Character cell width */
+ char YCharSize; /* Character cell height */
+ char NumberOfPlanes; /* Number of memory planes */
+ char BitsPerPixel; /* Bits per pixel */
+ char NumberOfBanks; /* Number of CGA style banks */
+ char MemoryModel; /* Memory model type */
+ char BankSize; /* Size of CGA style banks */
+ char NumberOfImagePages; /* Number of images pages */
+ char res1; /* Reserved */
+ char RedMaskSize; /* Size of direct color red mask */
+ char RedFieldPosition; /* Bit posn of lsb of red mask */
+ char GreenMaskSize; /* Size of direct color green mask */
+ char GreenFieldPosition; /* Bit posn of lsb of green mask */
+ char BlueMaskSize; /* Size of direct color blue mask */
+ char BlueFieldPosition; /* Bit posn of lsb of blue mask */
+ char RsvdMaskSize; /* Size of direct color res mask */
+ char RsvdFieldPosition; /* Bit posn of lsb of res mask */
+ char DirectColorModeInfo; /* Direct color mode attributes */
+
+ /* VBE 2.0 extensions */
+
+ long PhysBasePtr; /* Physical address for linear buf */
+ long OffScreenMemOffset; /* Pointer to start of offscreen mem*/
+ short OffScreenMemSize; /* Amount of offscreen mem in 1K's */
+ char res2[206]; /* Pad to 256 byte block size */
+ } VBE_modeInfo;
+
+#define vbeMemPK 4 /* Packed Pixel memory model */
+#define vbeUseLFB 0x4000 /* Enable linear framebuffer mode */
+
+/* Flags for the mode attributes returned by VBE_getModeInfo. If
+ * vbeMdNonBanked is set to 1 and vbeMdLinear is also set to 1, then only
+ * the linear framebuffer mode is available.
+ */
+
+#define vbeMdAvailable 0x0001 /* Video mode is available */
+#define vbeMdColorMode 0x0008 /* Mode is a color video mode */
+#define vbeMdGraphMode 0x0010 /* Mode is a graphics mode */
+#define vbeMdNonBanked 0x0040 /* Banked mode is not supported */
+#define vbeMdLinear 0x0080 /* Linear mode supported */
+
+/* Structures for issuing real mode interrupts with DPMI */
+
+struct _RMWORDREGS {
+ unsigned short ax, bx, cx, dx, si, di, cflag;
+ };
+
+struct _RMBYTEREGS {
+ unsigned char al, ah, bl, bh, cl, ch, dl, dh;
+ };
+
+typedef union {
+ struct _RMWORDREGS x;
+ struct _RMBYTEREGS h;
+ } RMREGS;
+
+typedef struct {
+ unsigned short es;
+ unsigned short cs;
+ unsigned short ss;
+ unsigned short ds;
+ } RMSREGS;
+
+/* Inline assembler block fill/move routines */
+
+void LfbMemset(void *p,int c,int n);
+#pragma aux LfbMemset = \
+ "shr ecx,2" \
+ "xor eax,eax" \
+ "mov al,bl" \
+ "shl ebx,8" \
+ "or ax,bx" \
+ "mov ebx,eax" \
+ "shl ebx,16" \
+ "or eax,ebx" \
+ "rep stosd" \
+ parm [edi] [ebx] [ecx];
+
+void LfbMemcpy(void *dst,void *src,int n);
+#pragma aux LfbMemcpy = \
+ "shr ecx,2" \
+ "rep movsd" \
+ parm [edi] [esi] [ecx];
+
+/* Map a real mode pointer into address space */
+
+#define LfbMapRealPointer(p) (void*)(((unsigned)((p) & 0xFFFF0000) >> 12) + ((p) & 0xFFFF))
+
+/* Get the current timer tick count */
+
+#define LfbGetTicks() *((long*)0x46C)
+
+#pragma pack()
+
+#endif /* __LFBPROF_H */
diff --git a/kvm/vgabios/tests/testbios.c b/kvm/vgabios/tests/testbios.c
new file mode 100644
index 000000000..99da5a65f
--- /dev/null
+++ b/kvm/vgabios/tests/testbios.c
@@ -0,0 +1,353 @@
+/*
+ This is a little turbo C program that executes
+ several int10, and let you inspect the content
+ of the vgabios area
+
+ It is used to test the behavior of the vgabios
+*/
+
+#include <stdio.h>
+#include <dos.h>
+#include <conio.h>
+
+
+typedef unsigned char Bit8u;
+typedef unsigned short Bit16u;
+
+typedef struct
+{Bit8u initial;
+ Bit8u current;
+ Bit16u nbcols;
+ Bit16u regen;
+ Bit16u start;
+ Bit16u curpos[8];
+ Bit8u curtyp;
+ Bit8u curpage;
+ Bit16u crtc;
+ Bit16u msr;
+ Bit16u cgapal;
+ Bit8u nbrows;
+ Bit16u cheight;
+ Bit8u ctl;
+ Bit8u switches;
+ Bit8u modeset;
+ Bit8u dcc;
+ Bit16u vsseg;
+ Bit16u vsoffset;
+} BIOSAREA;
+
+void int10ax0003(struct REGPACK *regs)
+{
+ regs->r_ax=0x0003;
+ intr(0x10,regs);
+}
+
+void int10ax02(struct REGPACK *regs)
+{
+ regs->r_ax=0x0200;
+ regs->r_bx=0x0000;
+ regs->r_dx=0x1710;
+ intr(0x10,regs);
+ printf("We are now at 24/17");
+}
+
+void int10ax03(struct REGPACK *regs)
+{
+ regs->r_ax=0x0300;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+ printf("\nCursor is ax%04x cx%04x dx%04x\n",regs->r_ax,regs->r_cx,regs->r_dx);
+}
+
+void int10ax0501(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0e61;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+ printf("We are now on page 2");
+}
+
+void int10ax0602(struct REGPACK *regs)
+{
+ regs->r_ax=0x0602;
+ regs->r_bx=0x0700;
+ regs->r_cx=0x0101;
+ regs->r_dx=0x0a0a;
+ intr(0x10,regs);
+ printf("Scrolled 2 up");
+}
+
+void int10ax0702(struct REGPACK *regs)
+{
+ regs->r_ax=0x0702;
+ regs->r_bx=0x0700;
+ regs->r_cx=0x0101;
+ regs->r_dx=0x0a0a;
+ intr(0x10,regs);
+ printf("Scrolled 2 down");
+}
+
+void int10ax08(struct REGPACK *regs)
+{
+ regs->r_ax=0x0800;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+}
+
+void int10ax09(struct REGPACK *regs)
+{
+ char attr;
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ for(attr=0;attr<16;attr++)
+ {printf("%02x ",attr);
+ regs->r_ax=0x0961+attr;
+ regs->r_bx=0x0100+attr;
+ regs->r_cx=0x0016;
+ intr(0x10,regs);
+ printf("\n");
+ }
+}
+
+void int10ax0a(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0a62;
+ regs->r_bx=0x0101;
+ regs->r_cx=0x0016;
+ intr(0x10,regs);
+}
+
+void int10ax0f(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0f00;
+ intr(0x10,regs);
+}
+
+void int10ax1b(struct REGPACK *regs)
+{unsigned char table[64];
+ unsigned char far *ptable;
+ int i;
+
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x1b00;
+ regs->r_bx=0x0000;
+ ptable=&table;
+ regs->r_es=FP_SEG(ptable);
+ regs->r_di=FP_OFF(ptable);
+ printf("Read state info in %04x:%04x\n",regs->r_es,regs->r_di);
+ intr(0x10,regs);
+
+ for(i=0;i<64;i++)
+ {if(i%16==0)printf("\n%02x ",i);
+ printf("%02x ",table[i]);
+ }
+ printf("\n");
+}
+
+static unsigned char var[64];
+
+void int10ax13(struct REGPACK *regs)
+{unsigned char far *pvar;
+
+ pvar=&var;
+
+ regs->r_ax=0x1300;
+ regs->r_bx=0x000b;
+ regs->r_dx=0x1010;
+ regs->r_cx=0x0002;
+ regs->r_es=FP_SEG(pvar);
+ regs->r_bp=FP_OFF(pvar);
+ pokeb(regs->r_es,regs->r_bp,'t');
+ pokeb(regs->r_es,regs->r_bp+1,'b');
+ printf("Writing from %04x:%04x\n",regs->r_es,regs->r_bp);
+ intr(0x10,regs);
+
+}
+
+void switch_50(struct REGPACK *regs)
+{
+ regs->r_ax=0x1202;
+ regs->r_bx=0x3000;
+ intr(0x10,regs);
+ regs->r_ax=0x0003;
+ intr(0x10,regs);
+ regs->r_ax=0x1112;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+}
+
+char exec_function(struct REGPACK *regs)
+{char c;
+
+ printf("--- Functions --------------------\n");
+ printf("a. int10 ax0003\t");
+ printf("b. int10 ax02\t");
+ printf("c. int10 ax03\t");
+ printf("d. int10 ax0501\n");
+ printf("e. int10 ax0602\t");
+ printf("f. int10 ax0702\t");
+ printf("g. int10 ax08\t");
+ printf("h. int10 ax09\t");
+ printf("i. int10 ax0a\n");
+ printf("j. int10 ax0f\t");
+ printf("k. int10 ax1b\t");
+ printf("l. int10 ax13\n");
+ printf("q. Quit\t");
+ printf("r. switch to 50 lines\n");
+ c=getche();
+
+ switch(c)
+ {case 'a':
+ int10ax0003(regs);
+ break;
+ case 'b':
+ int10ax02(regs);
+ break;
+ case 'c':
+ int10ax03(regs);
+ break;
+ case 'd':
+ int10ax0501(regs);
+ break;
+ case 'e':
+ int10ax0602(regs);
+ break;
+ case 'f':
+ int10ax0702(regs);
+ break;
+ case 'g':
+ int10ax08(regs);
+ break;
+ case 'h':
+ int10ax09(regs);
+ break;
+ case 'i':
+ int10ax0a(regs);
+ break;
+ case 'j':
+ int10ax0f(regs);
+ break;
+ case 'k':
+ int10ax1b(regs);
+ break;
+ case 'l':
+ int10ax13(regs);
+ break;
+ case 'q':
+ break;
+ case 'r':
+ switch_50(regs);
+ break;
+ default:
+ printf("No such function!\n");
+ }
+
+ if(c=='q')return 1;
+ while(kbhit()==0);
+ c=getch();
+
+ return 0;
+}
+
+void read_bios_area(BIOSAREA *biosarea)
+{
+ biosarea->initial=peekb(0x40,0x10);
+ biosarea->current=peekb(0x40,0x49);
+ biosarea->nbcols=peek(0x40,0x4a);
+ biosarea->regen=peek(0x40,0x4c);
+ biosarea->start=peek(0x40,0x4e);
+ biosarea->curpos[0]=peek(0x40,0x50);
+ biosarea->curpos[1]=peek(0x40,0x52);
+ biosarea->curpos[2]=peek(0x40,0x54);
+ biosarea->curpos[3]=peek(0x40,0x56);
+ biosarea->curpos[4]=peek(0x40,0x58);
+ biosarea->curpos[5]=peek(0x40,0x5a);
+ biosarea->curpos[6]=peek(0x40,0x5c);
+ biosarea->curpos[7]=peek(0x40,0x5e);
+ biosarea->curtyp=peek(0x40,0x60);
+ biosarea->curpage=peekb(0x40,0x62);
+ biosarea->crtc=peek(0x40,0x63);
+ biosarea->msr=peekb(0x40,0x65);
+ biosarea->cgapal=peekb(0x40,0x66);
+ biosarea->nbrows=peekb(0x40,0x84);
+ biosarea->cheight=peek(0x40,0x85);
+ biosarea->ctl=peekb(0x40,0x87);
+ biosarea->switches=peekb(0x40,0x88);
+ biosarea->modeset=peekb(0x40,0x89);
+ biosarea->dcc=peekb(0x40,0x8a);
+ biosarea->vsseg=peek(0x40,0xa8);
+ biosarea->vsoffset=peek(0x40,0xaa);
+}
+
+void show_bios_area(BIOSAREA *biosarea)
+{
+ printf("--- BIOS area --------------------\n");
+ printf("initial : %02x\t",biosarea->initial);
+ printf("current : %02x\t",biosarea->current);
+ printf("nbcols : %04x\t",biosarea->nbcols);
+ printf("regen : %04x\t",biosarea->regen);
+ printf("start : %04x\n",biosarea->start);
+ printf("curpos : %04x %04x %04x %04x %04x %04x %04x %04x\n",
+ biosarea->curpos[0], biosarea->curpos[1], biosarea->curpos[2], biosarea->curpos[3],
+ biosarea->curpos[4], biosarea->curpos[5], biosarea->curpos[6], biosarea->curpos[7]);
+ printf("curtyp : %04x\t",biosarea->curtyp);
+ printf("curpage : %02x\t",biosarea->curpage);
+ printf("crtc : %04x\t",biosarea->crtc);
+ printf("msr : %04x\n",biosarea->msr);
+ printf("cgapal : %04x\t",biosarea->cgapal);
+ printf("nbrows-1: %02x\t",biosarea->nbrows);
+ printf("cheight : %04x\t",biosarea->cheight);
+ printf("ctl : %02x\n",biosarea->ctl);
+ printf("switches: %02x\t",biosarea->switches);
+ printf("modeset : %02x\t",biosarea->modeset);
+ printf("dcc : %02x\t",biosarea->dcc);
+ printf("vs : %04x:%04x\n",biosarea->vsseg,biosarea->vsoffset);
+}
+
+void show_regs(struct REGPACK *regs)
+{
+ printf("--- Registers --------------------\n");
+ printf("ax %04x\t",regs->r_ax);
+ printf("bx %04x\t",regs->r_bx);
+ printf("cx %04x\t",regs->r_cx);
+ printf("dx %04x\t",regs->r_dx);
+ printf("ds %04x\t",regs->r_ds);
+ printf("si %04x\t",regs->r_si);
+ printf("es %04x\t",regs->r_es);
+ printf("di %04x\n",regs->r_di);
+}
+
+void reset_videomode()
+{
+ struct REGPACK regs;
+
+ regs.r_ax=0x0003;
+ intr(0x10,&regs);
+}
+
+void main()
+{
+
+ BIOSAREA biosarea;
+ struct REGPACK regs;
+
+ directvideo=0;
+
+ while(1)
+ {
+ read_bios_area(&biosarea);
+
+ reset_videomode();
+ show_bios_area(&biosarea);
+ show_regs(&regs);
+
+ if(exec_function(&regs)!=0)break;
+ }
+}
diff --git a/kvm/vgabios/vbe.c b/kvm/vgabios/vbe.c
new file mode 100644
index 000000000..6173ca033
--- /dev/null
+++ b/kvm/vgabios/vbe.c
@@ -0,0 +1,1432 @@
+// ============================================================================================
+//
+// Copyright (C) 2002 Jeroen Janssen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// ============================================================================================
+//
+// This VBE is part of the VGA Bios specific to the plex86/bochs Emulated VGA card.
+// You can NOT drive any physical vga card with it.
+//
+// ============================================================================================
+//
+// This VBE Bios is based on information taken from :
+// - VESA BIOS EXTENSION (VBE) Core Functions Standard Version 3.0 located at www.vesa.org
+//
+// ============================================================================================
+
+
+// defines available
+
+// disable VESA/VBE2 check in vbe info
+//#define VBE2_NO_VESA_CHECK
+
+
+#include "vbe.h"
+#include "vbetables.h"
+
+#define VBE_TOTAL_VIDEO_MEMORY_DIV_64K (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB*1024/64)
+
+// The current OEM Software Revision of this VBE Bios
+#define VBE_OEM_SOFTWARE_REV 0x0002;
+
+extern char vbebios_copyright;
+extern char vbebios_vendor_name;
+extern char vbebios_product_name;
+extern char vbebios_product_revision;
+
+ASM_START
+// FIXME: 'merge' these (c) etc strings with the vgabios.c strings?
+_vbebios_copyright:
+.ascii "Bochs/Plex86 VBE(C) 2003 http://savannah.nongnu.org/projects/vgabios/"
+.byte 0x00
+
+_vbebios_vendor_name:
+.ascii "Bochs/Plex86 Developers"
+.byte 0x00
+
+_vbebios_product_name:
+.ascii "Bochs/Plex86 VBE Adapter"
+.byte 0x00
+
+_vbebios_product_revision:
+.ascii "$Id$"
+.byte 0x00
+
+_vbebios_info_string:
+.ascii "Bochs VBE Display Adapter enabled"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+_no_vbebios_info_string:
+.ascii "NO Bochs VBE Support available!"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+msg_vbe_init:
+.ascii "VBE Bios $Id$"
+.byte 0x0a,0x0d, 0x00
+#endif
+
+ .align 2
+vesa_pm_start:
+ dw vesa_pm_set_window - vesa_pm_start
+ dw vesa_pm_set_display_start - vesa_pm_start
+ dw vesa_pm_unimplemented - vesa_pm_start
+ dw vesa_pm_io_ports_table - vesa_pm_start
+vesa_pm_io_ports_table:
+ dw VBE_DISPI_IOPORT_INDEX
+ dw VBE_DISPI_IOPORT_INDEX + 1
+ dw VBE_DISPI_IOPORT_DATA
+ dw VBE_DISPI_IOPORT_DATA + 1
+ dw 0xffff
+ dw 0xffff
+
+ USE32
+vesa_pm_set_window:
+ cmp bx, #0x00
+ je vesa_pm_set_display_window1
+ mov ax, #0x0100
+ ret
+vesa_pm_set_display_window1:
+ mov ax, dx
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BANK
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ in ax, dx
+ pop dx
+ cmp dx, ax
+ jne illegal_window
+ mov ax, #0x004f
+ ret
+illegal_window:
+ mov ax, #0x014f
+ ret
+
+vesa_pm_set_display_start:
+ cmp bl, #0x80
+ je vesa_pm_set_display_start1
+ cmp bl, #0x00
+ je vesa_pm_set_display_start1
+ mov ax, #0x0100
+ ret
+vesa_pm_set_display_start1:
+; convert offset to (X, Y) coordinate
+; (would be simpler to change Bochs VBE API...)
+ push eax
+ push ecx
+ push edx
+ push esi
+ push edi
+ shl edx, #16
+ and ecx, #0xffff
+ or ecx, edx
+ shl ecx, #2
+ mov eax, ecx
+
+ push eax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ movzx ecx, ax
+
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ movzx esi, ax
+ pop eax
+
+ cmp esi, #4
+ jz bpp4_mode
+ add esi, #7
+ shr esi, #3
+ imul ecx, esi
+ xor edx, edx
+ div ecx
+ mov edi, eax
+ mov eax, edx
+ xor edx, edx
+ div esi
+ jmp set_xy_regs
+
+bpp4_mode:
+ shr ecx, #1
+ xor edx, edx
+ div ecx
+ mov edi, eax
+ mov eax, edx
+ shl eax, #1
+
+set_xy_regs:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_X_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+
+ mov ax, di
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop eax
+ mov ax, #0x004f
+ ret
+
+vesa_pm_unimplemented:
+ mov ax, #0x014f
+ ret
+ USE16
+vesa_pm_end:
+
+; DISPI ioport functions
+
+dispi_get_id:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ID
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+dispi_set_id:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ID
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+ASM_END
+
+static void dispi_set_xres(xres)
+ Bit16u xres;
+{
+ASM_START
+ push bp
+ mov bp, sp
+ push ax
+ push dx
+
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_XRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ mov ax, 4[bp] ; xres
+ out dx, ax
+
+ pop dx
+ pop ax
+ pop bp
+ASM_END
+}
+
+static void dispi_set_yres(yres)
+ Bit16u yres;
+{
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_YRES);
+ outw(VBE_DISPI_IOPORT_DATA,yres);
+}
+
+static void dispi_set_bpp(bpp)
+ Bit16u bpp;
+{
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_BPP);
+ outw(VBE_DISPI_IOPORT_DATA,bpp);
+}
+
+ASM_START
+; AL = bits per pixel / AH = bytes per pixel
+dispi_get_bpp:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ mov ah, al
+ shr ah, 3
+ test al, #0x07
+ jz get_bpp_noinc
+ inc ah
+get_bpp_noinc:
+ pop dx
+ ret
+
+; get display capabilities
+
+_dispi_get_max_xres:
+ push dx
+ push bx
+ call dispi_get_enable
+ mov bx, ax
+ or ax, # VBE_DISPI_GETCAPS
+ call _dispi_set_enable
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_XRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ push ax
+ mov ax, bx
+ call _dispi_set_enable
+ pop ax
+ pop bx
+ pop dx
+ ret
+
+_dispi_get_max_bpp:
+ push dx
+ push bx
+ call dispi_get_enable
+ mov bx, ax
+ or ax, # VBE_DISPI_GETCAPS
+ call _dispi_set_enable
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ push ax
+ mov ax, bx
+ call _dispi_set_enable
+ pop ax
+ pop bx
+ pop dx
+ ret
+
+_dispi_set_enable:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ENABLE
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_enable:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ENABLE
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+_dispi_set_bank:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BANK
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_bank:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BANK
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+ASM_END
+
+static void dispi_set_bank_farcall()
+{
+ASM_START
+ cmp bx,#0x0100
+ je dispi_set_bank_farcall_get
+ or bx,bx
+ jnz dispi_set_bank_farcall_error
+ mov ax,dx
+ push dx
+ push ax
+ mov ax,# VBE_DISPI_INDEX_BANK
+ mov dx,# VBE_DISPI_IOPORT_INDEX
+ out dx,ax
+ pop ax
+ mov dx,# VBE_DISPI_IOPORT_DATA
+ out dx,ax
+ in ax,dx
+ pop dx
+ cmp dx,ax
+ jne dispi_set_bank_farcall_error
+ mov ax, #0x004f
+ retf
+dispi_set_bank_farcall_get:
+ mov ax,# VBE_DISPI_INDEX_BANK
+ mov dx,# VBE_DISPI_IOPORT_INDEX
+ out dx,ax
+ mov dx,# VBE_DISPI_IOPORT_DATA
+ in ax,dx
+ mov dx,ax
+ retf
+dispi_set_bank_farcall_error:
+ mov ax,#0x014F
+ retf
+ASM_END
+}
+
+ASM_START
+dispi_set_x_offset:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_X_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_x_offset:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_X_OFFSET
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+dispi_set_y_offset:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_y_offset:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+vga_set_virt_width:
+ push ax
+ push bx
+ push dx
+ mov bx, ax
+ call dispi_get_bpp
+ cmp al, #0x04
+ ja set_width_svga
+ shr bx, #1
+set_width_svga:
+ shr bx, #3
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ah, bl
+ mov al, #0x13
+ out dx, ax
+ pop dx
+ pop bx
+ pop ax
+ ret
+
+dispi_set_virt_width:
+ call vga_set_virt_width
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_virt_width:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+dispi_get_virt_height:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_HEIGHT
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+_vga_compat_setup:
+ push ax
+ push dx
+
+ ; set CRT X resolution
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_XRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ push ax
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ax, #0x0011
+ out dx, ax
+ pop ax
+ push ax
+ shr ax, #3
+ dec ax
+ mov ah, al
+ mov al, #0x01
+ out dx, ax
+ pop ax
+ call vga_set_virt_width
+
+ ; set CRT Y resolution
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_YRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ dec ax
+ push ax
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ah, al
+ mov al, #0x12
+ out dx, ax
+ pop ax
+ mov al, #0x07
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0xbd
+ test ah, #0x01
+ jz bit8_clear
+ or al, #0x02
+bit8_clear:
+ test ah, #0x02
+ jz bit9_clear
+ or al, #0x40
+bit9_clear:
+ out dx, al
+
+ ; other settings
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ax, #0x0009
+ out dx, ax
+ mov al, #0x17
+ out dx, al
+ mov dx, # VGAREG_VGA_CRTC_DATA
+ in al, dx
+ or al, #0x03
+ out dx, al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ or al, #0x01
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0x0506
+ out dx, ax
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0f02
+ out dx, ax
+
+ ; settings for >= 8bpp
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ cmp al, #0x08
+ jb vga_compat_end
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov al, #0x14
+ out dx, al
+ mov dx, # VGAREG_VGA_CRTC_DATA
+ in al, dx
+ or al, #0x40
+ out dx, al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ or al, #0x40
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov al, #0x04
+ out dx, al
+ mov dx, # VGAREG_SEQU_DATA
+ in al, dx
+ or al, #0x08
+ out dx, al
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov al, #0x05
+ out dx, al
+ mov dx, # VGAREG_GRDC_DATA
+ in al, dx
+ and al, #0x9f
+ or al, #0x40
+ out dx, al
+
+vga_compat_end:
+ pop dx
+ pop ax
+ASM_END
+
+
+// ModeInfo helper function
+static ModeInfoListItem* mode_info_find_mode(mode, using_lfb)
+ Bit16u mode; Boolean using_lfb;
+{
+ ModeInfoListItem *cur_info=&mode_info_list;
+
+ while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST)
+ {
+ if (cur_info->mode == mode)
+ {
+ if (!using_lfb)
+ {
+ return cur_info;
+ }
+ else if (cur_info->info.ModeAttributes & VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE)
+ {
+ return cur_info;
+ }
+ else
+ {
+ cur_info++;
+ }
+ }
+ else
+ {
+ cur_info++;
+ }
+ }
+
+ return 0;
+}
+
+ASM_START
+
+; Has VBE display - Returns true if VBE display detected
+
+_vbe_has_vbe_display:
+ push ds
+ push bx
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_VBE_FLAG
+ mov al, [bx]
+ and al, #0x01
+ xor ah, ah
+ pop bx
+ pop ds
+ ret
+
+; VBE Init - Initialise the Vesa Bios Extension Code
+; This function does a sanity check on the host side display code interface.
+
+vbe_init:
+ mov ax, # VBE_DISPI_ID0
+ call dispi_set_id
+ call dispi_get_id
+ cmp ax, # VBE_DISPI_ID0
+ jne no_vbe_interface
+ push ds
+ push bx
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_VBE_FLAG
+ mov al, #0x01
+ mov [bx], al
+ pop bx
+ pop ds
+ mov ax, # VBE_DISPI_ID4
+ call dispi_set_id
+no_vbe_interface:
+#if defined(USE_BX_INFO) || defined(DEBUG)
+ mov bx, #msg_vbe_init
+ push bx
+ call _printf
+ inc sp
+ inc sp
+#endif
+ ret
+
+; VBE Display Info - Display information on screen about the VBE
+
+vbe_display_info:
+ call _vbe_has_vbe_display
+ test ax, ax
+ jz no_vbe_flag
+ mov ax, #0xc000
+ mov ds, ax
+ mov si, #_vbebios_info_string
+ jmp _display_string
+no_vbe_flag:
+ mov ax, #0xc000
+ mov ds, ax
+ mov si, #_no_vbebios_info_string
+ jmp _display_string
+ASM_END
+
+/** Function 00h - Return VBE Controller Information
+ *
+ * Input:
+ * AX = 4F00h
+ * ES:DI = Pointer to buffer in which to place VbeInfoBlock structure
+ * (VbeSignature should be VBE2 when VBE 2.0 information is desired and
+ * the info block is 512 bytes in size)
+ * Output:
+ * AX = VBE Return Status
+ *
+ */
+void vbe_biosfn_return_controller_information(AX, ES, DI)
+Bit16u *AX;Bit16u ES;Bit16u DI;
+{
+ Bit16u ss=get_SS();
+ VbeInfoBlock vbe_info_block;
+ Bit16u status;
+ Bit16u result;
+ Bit16u vbe2_info;
+ Bit16u cur_mode=0;
+ Bit16u cur_ptr=34;
+ ModeInfoListItem *cur_info=&mode_info_list;
+
+ status = read_word(ss, AX);
+
+#ifdef DEBUG
+ printf("VBE vbe_biosfn_return_vbe_info ES%x DI%x AX%x\n",ES,DI,status);
+#endif
+
+ vbe2_info = 0;
+#ifdef VBE2_NO_VESA_CHECK
+#else
+ // get vbe_info_block into local variable
+ memcpyb(ss, &vbe_info_block, ES, DI, sizeof(vbe_info_block));
+
+ // check for VBE2 signature
+ if (((vbe_info_block.VbeSignature[0] == 'V') &&
+ (vbe_info_block.VbeSignature[1] == 'B') &&
+ (vbe_info_block.VbeSignature[2] == 'E') &&
+ (vbe_info_block.VbeSignature[3] == '2')) ||
+
+ ((vbe_info_block.VbeSignature[0] == 'V') &&
+ (vbe_info_block.VbeSignature[1] == 'E') &&
+ (vbe_info_block.VbeSignature[2] == 'S') &&
+ (vbe_info_block.VbeSignature[3] == 'A')) )
+ {
+ vbe2_info = 1;
+#ifdef DEBUG
+ printf("VBE correct VESA/VBE2 signature found\n");
+#endif
+ }
+#endif
+
+ // VBE Signature
+ vbe_info_block.VbeSignature[0] = 'V';
+ vbe_info_block.VbeSignature[1] = 'E';
+ vbe_info_block.VbeSignature[2] = 'S';
+ vbe_info_block.VbeSignature[3] = 'A';
+
+ // VBE Version supported
+ vbe_info_block.VbeVersion = 0x0200;
+
+ // OEM String
+ vbe_info_block.OemStringPtr_Seg = 0xc000;
+ vbe_info_block.OemStringPtr_Off = &vbebios_copyright;
+
+ // Capabilities
+ vbe_info_block.Capabilities[0] = VBE_CAPABILITY_8BIT_DAC;
+ vbe_info_block.Capabilities[1] = 0;
+ vbe_info_block.Capabilities[2] = 0;
+ vbe_info_block.Capabilities[3] = 0;
+
+ // VBE Video Mode Pointer (dynamicly generated from the mode_info_list)
+ vbe_info_block.VideoModePtr_Seg= ES ;
+ vbe_info_block.VideoModePtr_Off= DI + 34;
+
+ // VBE Total Memory (in 64b blocks)
+ vbe_info_block.TotalMemory = VBE_TOTAL_VIDEO_MEMORY_DIV_64K;
+
+ if (vbe2_info)
+ {
+ // OEM Stuff
+ vbe_info_block.OemSoftwareRev = VBE_OEM_SOFTWARE_REV;
+ vbe_info_block.OemVendorNamePtr_Seg = 0xc000;
+ vbe_info_block.OemVendorNamePtr_Off = &vbebios_vendor_name;
+ vbe_info_block.OemProductNamePtr_Seg = 0xc000;
+ vbe_info_block.OemProductNamePtr_Off = &vbebios_product_name;
+ vbe_info_block.OemProductRevPtr_Seg = 0xc000;
+ vbe_info_block.OemProductRevPtr_Off = &vbebios_product_revision;
+
+ // copy updates in vbe_info_block back
+ memcpyb(ES, DI, ss, &vbe_info_block, sizeof(vbe_info_block));
+ }
+ else
+ {
+ // copy updates in vbe_info_block back (VBE 1.x compatibility)
+ memcpyb(ES, DI, ss, &vbe_info_block, 256);
+ }
+
+ do
+ {
+ if ((cur_info->info.XResolution <= dispi_get_max_xres()) &&
+ (cur_info->info.BitsPerPixel <= dispi_get_max_bpp())) {
+#ifdef DEBUG
+ printf("VBE found mode %x => %x\n", cur_info->mode,cur_mode);
+#endif
+ write_word(ES, DI + cur_ptr, cur_info->mode);
+ cur_mode++;
+ cur_ptr+=2;
+ } else {
+#ifdef DEBUG
+ printf("VBE mode %x (xres=%x / bpp=%02x) not supported by display\n", cur_info->mode,cur_info->info.XResolution,cur_info->info.BitsPerPixel);
+#endif
+ }
+ cur_info++;
+ } while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST);
+
+ // Add vesa mode list terminator
+ write_word(ES, DI + cur_ptr, cur_info->mode);
+
+ result = 0x4f;
+
+ write_word(ss, AX, result);
+}
+
+
+/** Function 01h - Return VBE Mode Information
+ *
+ * Input:
+ * AX = 4F01h
+ * CX = Mode Number
+ * ES:DI = Pointer to buffer in which to place ModeInfoBlock structure
+ * Output:
+ * AX = VBE Return Status
+ *
+ */
+void vbe_biosfn_return_mode_information(AX, CX, ES, DI)
+Bit16u *AX;Bit16u CX; Bit16u ES;Bit16u DI;
+{
+ Bit16u result=0x0100;
+ Bit16u ss=get_SS();
+ ModeInfoBlock info;
+ ModeInfoListItem *cur_info;
+ Boolean using_lfb;
+
+#ifdef DEBUG
+ printf("VBE vbe_biosfn_return_mode_information ES%x DI%x CX%x\n",ES,DI,CX);
+#endif
+
+ using_lfb=((CX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER);
+
+ CX = (CX & 0x1ff);
+
+ cur_info = mode_info_find_mode(CX, using_lfb, &cur_info);
+
+ if (cur_info != 0)
+ {
+#ifdef DEBUG
+ printf("VBE found mode %x\n",CX);
+#endif
+ memsetb(ss, &info, 0, sizeof(ModeInfoBlock));
+ memcpyb(ss, &info, 0xc000, &(cur_info->info), sizeof(ModeInfoBlockCompact));
+ if (using_lfb) {
+ info.NumberOfBanks = 1;
+ }
+ if (info.WinAAttributes & VBE_WINDOW_ATTRIBUTE_RELOCATABLE) {
+ info.WinFuncPtr = 0xC0000000UL;
+ *(Bit16u *)&(info.WinFuncPtr) = (Bit16u)(dispi_set_bank_farcall);
+ }
+
+ result = 0x4f;
+ }
+ else
+ {
+#ifdef DEBUG
+ printf("VBE *NOT* found mode %x\n",CX);
+#endif
+ result = 0x100;
+ }
+
+ if (result == 0x4f)
+ {
+ // copy updates in mode_info_block back
+ memcpyb(ES, DI, ss, &info, sizeof(info));
+ }
+
+ write_word(ss, AX, result);
+}
+
+/** Function 02h - Set VBE Mode
+ *
+ * Input:
+ * AX = 4F02h
+ * BX = Desired Mode to set
+ * ES:DI = Pointer to CRTCInfoBlock structure
+ * Output:
+ * AX = VBE Return Status
+ *
+ */
+void vbe_biosfn_set_mode(AX, BX, ES, DI)
+Bit16u *AX;Bit16u BX; Bit16u ES;Bit16u DI;
+{
+ Bit16u ss = get_SS();
+ Bit16u result;
+ ModeInfoListItem *cur_info;
+ Boolean using_lfb;
+ Bit8u no_clear;
+ Bit8u lfb_flag;
+
+ using_lfb=((BX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER);
+ lfb_flag=using_lfb?VBE_DISPI_LFB_ENABLED:0;
+ no_clear=((BX & VBE_MODE_PRESERVE_DISPLAY_MEMORY) == VBE_MODE_PRESERVE_DISPLAY_MEMORY)?VBE_DISPI_NOCLEARMEM:0;
+
+ BX = (BX & 0x1ff);
+
+ //result=read_word(ss,AX);
+
+ // check for non vesa mode
+ if (BX<VBE_MODE_VESA_DEFINED)
+ {
+ Bit8u mode;
+
+ dispi_set_enable(VBE_DISPI_DISABLED);
+ // call the vgabios in order to set the video mode
+ // this allows for going back to textmode with a VBE call (some applications expect that to work)
+
+ mode=(BX & 0xff);
+ biosfn_set_video_mode(mode);
+ result = 0x4f;
+ }
+
+ cur_info = mode_info_find_mode(BX, using_lfb, &cur_info);
+
+ if (cur_info != 0)
+ {
+#ifdef DEBUG
+ printf("VBE found mode %x, setting:\n", BX);
+ printf("\txres%x yres%x bpp%x\n",
+ cur_info->info.XResolution,
+ cur_info->info.YResolution,
+ cur_info->info.BitsPerPixel);
+#endif
+
+ // first disable current mode (when switching between vesa modi)
+ dispi_set_enable(VBE_DISPI_DISABLED);
+
+ if (cur_info->info.BitsPerPixel == 4)
+ {
+ biosfn_set_video_mode(0x6a);
+ }
+
+ dispi_set_bpp(cur_info->info.BitsPerPixel);
+ dispi_set_xres(cur_info->info.XResolution);
+ dispi_set_yres(cur_info->info.YResolution);
+ dispi_set_bank(0);
+ dispi_set_enable(VBE_DISPI_ENABLED | no_clear | lfb_flag);
+ vga_compat_setup();
+
+ write_word(BIOSMEM_SEG,BIOSMEM_VBE_MODE,BX);
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60 | no_clear));
+
+ result = 0x4f;
+ }
+ else
+ {
+#ifdef DEBUG
+ printf("VBE *NOT* found mode %x\n" , BX);
+#endif
+ result = 0x100;
+
+ // FIXME: redirect non VBE modi to normal VGA bios operation
+ // (switch back to VGA mode
+ if (BX == 3)
+ result = 0x4f;
+ }
+
+ write_word(ss, AX, result);
+}
+
+/** Function 03h - Return Current VBE Mode
+ *
+ * Input:
+ * AX = 4F03h
+ * Output:
+ * AX = VBE Return Status
+ * BX = Current VBE Mode
+ *
+ */
+ASM_START
+vbe_biosfn_return_current_mode:
+ push ds
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ call dispi_get_enable
+ and ax, # VBE_DISPI_ENABLED
+ jz no_vbe_mode
+ mov bx, # BIOSMEM_VBE_MODE
+ mov ax, [bx]
+ mov bx, ax
+ jnz vbe_03_ok
+no_vbe_mode:
+ mov bx, # BIOSMEM_CURRENT_MODE
+ mov al, [bx]
+ mov bl, al
+ xor bh, bh
+vbe_03_ok:
+ mov ax, #0x004f
+ pop ds
+ ret
+ASM_END
+
+
+Bit16u vbe_biosfn_read_video_state_size()
+{
+ return 9 * 2;
+}
+
+void vbe_biosfn_save_video_state(ES, BX)
+ Bit16u ES; Bit16u BX;
+{
+ Bit16u enable, i;
+
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
+ enable = inw(VBE_DISPI_IOPORT_DATA);
+ write_word(ES, BX, enable);
+ BX += 2;
+ if (!(enable & VBE_DISPI_ENABLED))
+ return;
+ for(i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
+ if (i != VBE_DISPI_INDEX_ENABLE) {
+ outw(VBE_DISPI_IOPORT_INDEX, i);
+ write_word(ES, BX, inw(VBE_DISPI_IOPORT_DATA));
+ BX += 2;
+ }
+ }
+}
+
+
+void vbe_biosfn_restore_video_state(ES, BX)
+ Bit16u ES; Bit16u BX;
+{
+ Bit16u enable, i;
+
+ enable = read_word(ES, BX);
+ BX += 2;
+
+ if (!(enable & VBE_DISPI_ENABLED)) {
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
+ outw(VBE_DISPI_IOPORT_DATA, enable);
+ } else {
+ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
+ outw(VBE_DISPI_IOPORT_DATA, enable);
+
+ for(i = VBE_DISPI_INDEX_BANK; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
+ outw(VBE_DISPI_IOPORT_INDEX, i);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ }
+ }
+}
+
+/** Function 04h - Save/Restore State
+ *
+ * Input:
+ * AX = 4F04h
+ * DL = 00h Return Save/Restore State buffer size
+ * 01h Save State
+ * 02h Restore State
+ * CX = Requested states
+ * ES:BX = Pointer to buffer (if DL <> 00h)
+ * Output:
+ * AX = VBE Return Status
+ * BX = Number of 64-byte blocks to hold the state buffer (if DL=00h)
+ *
+ */
+void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX)
+Bit16u *AX; Bit16u CX; Bit16u DX; Bit16u ES; Bit16u *BX;
+{
+ Bit16u ss=get_SS();
+ Bit16u result, val;
+
+ result = 0x4f;
+ switch(GET_DL()) {
+ case 0x00:
+ val = biosfn_read_video_state_size2(CX);
+#ifdef DEBUG
+ printf("VGA state size=%x\n", val);
+#endif
+ if (CX & 8)
+ val += vbe_biosfn_read_video_state_size();
+ write_word(ss, BX, val);
+ break;
+ case 0x01:
+ val = read_word(ss, BX);
+ val = biosfn_save_video_state(CX, ES, val);
+#ifdef DEBUG
+ printf("VGA save_state offset=%x\n", val);
+#endif
+ if (CX & 8)
+ vbe_biosfn_save_video_state(ES, val);
+ break;
+ case 0x02:
+ val = read_word(ss, BX);
+ val = biosfn_restore_video_state(CX, ES, val);
+#ifdef DEBUG
+ printf("VGA restore_state offset=%x\n", val);
+#endif
+ if (CX & 8)
+ vbe_biosfn_restore_video_state(ES, val);
+ break;
+ default:
+ // function failed
+ result = 0x100;
+ break;
+ }
+ write_word(ss, AX, result);
+}
+
+/** Function 05h - Display Window Control
+ *
+ * Input:
+ * AX = 4F05h
+ * (16-bit) BH = 00h Set memory window
+ * = 01h Get memory window
+ * BL = Window number
+ * = 00h Window A
+ * = 01h Window B
+ * DX = Window number in video memory in window
+ * granularity units (Set Memory Window only)
+ * Note:
+ * If this function is called while in a linear frame buffer mode,
+ * this function must fail with completion code AH=03h
+ *
+ * Output:
+ * AX = VBE Return Status
+ * DX = Window number in window granularity units
+ * (Get Memory Window only)
+ */
+ASM_START
+vbe_biosfn_display_window_control:
+ cmp bl, #0x00
+ jne vbe_05_failed
+ cmp bh, #0x01
+ je get_display_window
+ jb set_display_window
+ mov ax, #0x0100
+ ret
+set_display_window:
+ mov ax, dx
+ call _dispi_set_bank
+ call dispi_get_bank
+ cmp ax, dx
+ jne vbe_05_failed
+ mov ax, #0x004f
+ ret
+get_display_window:
+ call dispi_get_bank
+ mov dx, ax
+ mov ax, #0x004f
+ ret
+vbe_05_failed:
+ mov ax, #0x014f
+ ret
+ASM_END
+
+
+/** Function 06h - Set/Get Logical Scan Line Length
+ *
+ * Input:
+ * AX = 4F06h
+ * BL = 00h Set Scan Line Length in Pixels
+ * = 01h Get Scan Line Length
+ * = 02h Set Scan Line Length in Bytes
+ * = 03h Get Maximum Scan Line Length
+ * CX = If BL=00h Desired Width in Pixels
+ * If BL=02h Desired Width in Bytes
+ * (Ignored for Get Functions)
+ *
+ * Output:
+ * AX = VBE Return Status
+ * BX = Bytes Per Scan Line
+ * CX = Actual Pixels Per Scan Line
+ * (truncated to nearest complete pixel)
+ * DX = Maximum Number of Scan Lines
+ */
+ASM_START
+vbe_biosfn_set_get_logical_scan_line_length:
+ mov ax, cx
+ cmp bl, #0x01
+ je get_logical_scan_line_length
+ cmp bl, #0x02
+ je set_logical_scan_line_bytes
+ jb set_logical_scan_line_pixels
+ mov ax, #0x0100
+ ret
+set_logical_scan_line_bytes:
+ push ax
+ call dispi_get_bpp
+ xor bh, bh
+ mov bl, ah
+ or bl, bl
+ jnz no_4bpp_1
+ shl ax, #3
+ mov bl, #1
+no_4bpp_1:
+ xor dx, dx
+ pop ax
+ div bx
+set_logical_scan_line_pixels:
+ call dispi_set_virt_width
+get_logical_scan_line_length:
+ call dispi_get_bpp
+ xor bh, bh
+ mov bl, ah
+ call dispi_get_virt_width
+ mov cx, ax
+ or bl, bl
+ jnz no_4bpp_2
+ shr ax, #3
+ mov bl, #1
+no_4bpp_2:
+ mul bx
+ mov bx, ax
+ call dispi_get_virt_height
+ mov dx, ax
+ mov ax, #0x004f
+ ret
+ASM_END
+
+
+/** Function 07h - Set/Get Display Start
+ *
+ * Input(16-bit):
+ * AX = 4F07h
+ * BH = 00h Reserved and must be 00h
+ * BL = 00h Set Display Start
+ * = 01h Get Display Start
+ * = 02h Schedule Display Start (Alternate)
+ * = 03h Schedule Stereoscopic Display Start
+ * = 04h Get Scheduled Display Start Status
+ * = 05h Enable Stereoscopic Mode
+ * = 06h Disable Stereoscopic Mode
+ * = 80h Set Display Start during Vertical Retrace
+ * = 82h Set Display Start during Vertical Retrace (Alternate)
+ * = 83h Set Stereoscopic Display Start during Vertical Retrace
+ * ECX = If BL=02h/82h Display Start Address in bytes
+ * If BL=03h/83h Left Image Start Address in bytes
+ * EDX = If BL=03h/83h Right Image Start Address in bytes
+ * CX = If BL=00h/80h First Displayed Pixel In Scan Line
+ * DX = If BL=00h/80h First Displayed Scan Line
+ *
+ * Output:
+ * AX = VBE Return Status
+ * BH = If BL=01h Reserved and will be 0
+ * CX = If BL=01h First Displayed Pixel In Scan Line
+ * If BL=04h 0 if flip has not occurred, not 0 if it has
+ * DX = If BL=01h First Displayed Scan Line
+ *
+ * Input(32-bit):
+ * BH = 00h Reserved and must be 00h
+ * BL = 00h Set Display Start
+ * = 80h Set Display Start during Vertical Retrace
+ * CX = Bits 0-15 of display start address
+ * DX = Bits 16-31 of display start address
+ * ES = Selector for memory mapped registers
+ */
+ASM_START
+vbe_biosfn_set_get_display_start:
+ cmp bl, #0x80
+ je set_display_start
+ cmp bl, #0x01
+ je get_display_start
+ jb set_display_start
+ mov ax, #0x0100
+ ret
+set_display_start:
+ mov ax, cx
+ call dispi_set_x_offset
+ mov ax, dx
+ call dispi_set_y_offset
+ mov ax, #0x004f
+ ret
+get_display_start:
+ call dispi_get_x_offset
+ mov cx, ax
+ call dispi_get_y_offset
+ mov dx, ax
+ xor bh, bh
+ mov ax, #0x004f
+ ret
+ASM_END
+
+
+/** Function 08h - Set/Get Dac Palette Format
+ *
+ * Input:
+ * AX = 4F08h
+ * BL = 00h set DAC palette width
+ * = 01h get DAC palette width
+ * BH = If BL=00h: desired number of bits per primary color
+ * Output:
+ * AX = VBE Return Status
+ * BH = current number of bits per primary color (06h = standard VGA)
+ */
+ASM_START
+vbe_biosfn_set_get_dac_palette_format:
+ cmp bl, #0x01
+ je get_dac_palette_format
+ jb set_dac_palette_format
+ mov ax, #0x0100
+ ret
+set_dac_palette_format:
+ call dispi_get_enable
+ cmp bh, #0x06
+ je set_normal_dac
+ cmp bh, #0x08
+ jne vbe_08_unsupported
+ or ax, # VBE_DISPI_8BIT_DAC
+ jnz set_dac_mode
+set_normal_dac:
+ and ax, #~ VBE_DISPI_8BIT_DAC
+set_dac_mode:
+ call _dispi_set_enable
+get_dac_palette_format:
+ mov bh, #0x06
+ call dispi_get_enable
+ and ax, # VBE_DISPI_8BIT_DAC
+ jz vbe_08_ok
+ mov bh, #0x08
+vbe_08_ok:
+ mov ax, #0x004f
+ ret
+vbe_08_unsupported:
+ mov ax, #0x014f
+ ret
+ASM_END
+
+
+/** Function 09h - Set/Get Palette Data
+ *
+ * Input:
+ * AX = 4F09h
+ * Output:
+ * AX = VBE Return Status
+ *
+ * FIXME: incomplete API description, Input & Output
+ */
+void vbe_biosfn_set_get_palette_data(AX)
+{
+}
+
+/** Function 0Ah - Return VBE Protected Mode Interface
+ * Input: AX = 4F0Ah VBE 2.0 Protected Mode Interface
+ * BL = 00h Return protected mode table
+ *
+ *
+ * Output: AX = Status
+ * ES = Real Mode Segment of Table
+ * DI = Offset of Table
+ * CX = Length of Table including protected mode code
+ * (for copying purposes)
+ */
+ASM_START
+vbe_biosfn_return_protected_mode_interface:
+ test bl, bl
+ jnz _fail
+ mov di, #0xc000
+ mov es, di
+ mov di, # vesa_pm_start
+ mov cx, # vesa_pm_end
+ sub cx, di
+ mov ax, #0x004f
+ ret
+_fail:
+ mov ax, #0x014f
+ ret
+ASM_END
diff --git a/kvm/vgabios/vbe.h b/kvm/vgabios/vbe.h
new file mode 100644
index 000000000..60434ac7d
--- /dev/null
+++ b/kvm/vgabios/vbe.h
@@ -0,0 +1,313 @@
+#ifndef vbe_h_included
+#define vbe_h_included
+
+#include "vgabios.h"
+
+// DISPI helper function
+void dispi_set_enable(enable);
+
+/** VBE int10 API
+ *
+ * See the function descriptions in vbe.c for more information
+ */
+Boolean vbe_has_vbe_display();
+void vbe_biosfn_return_controller_information(AX, ES, DI);
+void vbe_biosfn_return_mode_information(AX, CX, ES, DI);
+void vbe_biosfn_set_mode(AX, BX, ES, DI);
+void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX);
+void vbe_biosfn_set_get_palette_data(AX);
+void vbe_biosfn_return_protected_mode_interface(AX);
+
+// The official VBE Information Block
+typedef struct VbeInfoBlock
+{
+ Bit8u VbeSignature[4];
+ Bit16u VbeVersion;
+ Bit16u OemStringPtr_Off;
+ Bit16u OemStringPtr_Seg;
+ Bit8u Capabilities[4];
+ Bit16u VideoModePtr_Off;
+ Bit16u VideoModePtr_Seg;
+ Bit16u TotalMemory;
+ Bit16u OemSoftwareRev;
+ Bit16u OemVendorNamePtr_Off;
+ Bit16u OemVendorNamePtr_Seg;
+ Bit16u OemProductNamePtr_Off;
+ Bit16u OemProductNamePtr_Seg;
+ Bit16u OemProductRevPtr_Off;
+ Bit16u OemProductRevPtr_Seg;
+ Bit16u Reserved[111]; // used for dynamicly generated mode list
+ Bit8u OemData[256];
+} VbeInfoBlock;
+
+
+// This one is for compactly storing a static list of mode info blocks
+// this saves us 189 bytes per block
+typedef struct ModeInfoBlockCompact
+{
+// Mandatory information for all VBE revisions
+ Bit16u ModeAttributes;
+ Bit8u WinAAttributes;
+ Bit8u WinBAttributes;
+ Bit16u WinGranularity;
+ Bit16u WinSize;
+ Bit16u WinASegment;
+ Bit16u WinBSegment;
+ Bit32u WinFuncPtr;
+ Bit16u BytesPerScanLine;
+// Mandatory information for VBE 1.2 and above
+ Bit16u XResolution;
+ Bit16u YResolution;
+ Bit8u XCharSize;
+ Bit8u YCharSize;
+ Bit8u NumberOfPlanes;
+ Bit8u BitsPerPixel;
+ Bit8u NumberOfBanks;
+ Bit8u MemoryModel;
+ Bit8u BankSize;
+ Bit8u NumberOfImagePages;
+ Bit8u Reserved_page;
+// Direct Color fields (required for direct/6 and YUV/7 memory models)
+ Bit8u RedMaskSize;
+ Bit8u RedFieldPosition;
+ Bit8u GreenMaskSize;
+ Bit8u GreenFieldPosition;
+ Bit8u BlueMaskSize;
+ Bit8u BlueFieldPosition;
+ Bit8u RsvdMaskSize;
+ Bit8u RsvdFieldPosition;
+ Bit8u DirectColorModeInfo;
+// Mandatory information for VBE 2.0 and above
+ Bit32u PhysBasePtr;
+ Bit32u OffScreenMemOffset;
+ Bit16u OffScreenMemSize;
+// Mandatory information for VBE 3.0 and above
+ Bit16u LinBytesPerScanLine;
+ Bit8u BnkNumberOfPages;
+ Bit8u LinNumberOfPages;
+ Bit8u LinRedMaskSize;
+ Bit8u LinRedFieldPosition;
+ Bit8u LinGreenMaskSize;
+ Bit8u LinGreenFieldPosition;
+ Bit8u LinBlueMaskSize;
+ Bit8u LinBlueFieldPosition;
+ Bit8u LinRsvdMaskSize;
+ Bit8u LinRsvdFieldPosition;
+ Bit32u MaxPixelClock;
+// Bit8u Reserved[189]; // DO NOT PUT THIS IN HERE because of Compact Mode Info storage in bios
+} ModeInfoBlockCompact;
+
+typedef struct ModeInfoBlock
+{
+// Mandatory information for all VBE revisions
+ Bit16u ModeAttributes;
+ Bit8u WinAAttributes;
+ Bit8u WinBAttributes;
+ Bit16u WinGranularity;
+ Bit16u WinSize;
+ Bit16u WinASegment;
+ Bit16u WinBSegment;
+ Bit32u WinFuncPtr;
+ Bit16u BytesPerScanLine;
+// Mandatory information for VBE 1.2 and above
+ Bit16u XResolution;
+ Bit16u YResolution;
+ Bit8u XCharSize;
+ Bit8u YCharSize;
+ Bit8u NumberOfPlanes;
+ Bit8u BitsPerPixel;
+ Bit8u NumberOfBanks;
+ Bit8u MemoryModel;
+ Bit8u BankSize;
+ Bit8u NumberOfImagePages;
+ Bit8u Reserved_page;
+// Direct Color fields (required for direct/6 and YUV/7 memory models)
+ Bit8u RedMaskSize;
+ Bit8u RedFieldPosition;
+ Bit8u GreenMaskSize;
+ Bit8u GreenFieldPosition;
+ Bit8u BlueMaskSize;
+ Bit8u BlueFieldPosition;
+ Bit8u RsvdMaskSize;
+ Bit8u RsvdFieldPosition;
+ Bit8u DirectColorModeInfo;
+// Mandatory information for VBE 2.0 and above
+ Bit32u PhysBasePtr;
+ Bit32u OffScreenMemOffset;
+ Bit16u OffScreenMemSize;
+// Mandatory information for VBE 3.0 and above
+ Bit16u LinBytesPerScanLine;
+ Bit8u BnkNumberOfPages;
+ Bit8u LinNumberOfPages;
+ Bit8u LinRedMaskSize;
+ Bit8u LinRedFieldPosition;
+ Bit8u LinGreenMaskSize;
+ Bit8u LinGreenFieldPosition;
+ Bit8u LinBlueMaskSize;
+ Bit8u LinBlueFieldPosition;
+ Bit8u LinRsvdMaskSize;
+ Bit8u LinRsvdFieldPosition;
+ Bit32u MaxPixelClock;
+ Bit8u Reserved[189];
+} ModeInfoBlock;
+
+typedef struct ModeInfoListItem
+{
+ Bit16u mode;
+ ModeInfoBlockCompact info;
+} ModeInfoListItem;
+
+// VBE Return Status Info
+// AL
+#define VBE_RETURN_STATUS_SUPPORTED 0x4F
+#define VBE_RETURN_STATUS_UNSUPPORTED 0x00
+// AH
+#define VBE_RETURN_STATUS_SUCCESSFULL 0x00
+#define VBE_RETURN_STATUS_FAILED 0x01
+#define VBE_RETURN_STATUS_NOT_SUPPORTED 0x02
+#define VBE_RETURN_STATUS_INVALID 0x03
+
+// VBE Mode Numbers
+
+#define VBE_MODE_VESA_DEFINED 0x0100
+#define VBE_MODE_REFRESH_RATE_USE_CRTC 0x0800
+#define VBE_MODE_LINEAR_FRAME_BUFFER 0x4000
+#define VBE_MODE_PRESERVE_DISPLAY_MEMORY 0x8000
+
+// VBE GFX Mode Number
+
+#define VBE_VESA_MODE_640X400X8 0x100
+#define VBE_VESA_MODE_640X480X8 0x101
+#define VBE_VESA_MODE_800X600X4 0x102
+#define VBE_VESA_MODE_800X600X8 0x103
+#define VBE_VESA_MODE_1024X768X4 0x104
+#define VBE_VESA_MODE_1024X768X8 0x105
+#define VBE_VESA_MODE_1280X1024X4 0x106
+#define VBE_VESA_MODE_1280X1024X8 0x107
+#define VBE_VESA_MODE_320X200X1555 0x10D
+#define VBE_VESA_MODE_320X200X565 0x10E
+#define VBE_VESA_MODE_320X200X888 0x10F
+#define VBE_VESA_MODE_640X480X1555 0x110
+#define VBE_VESA_MODE_640X480X565 0x111
+#define VBE_VESA_MODE_640X480X888 0x112
+#define VBE_VESA_MODE_800X600X1555 0x113
+#define VBE_VESA_MODE_800X600X565 0x114
+#define VBE_VESA_MODE_800X600X888 0x115
+#define VBE_VESA_MODE_1024X768X1555 0x116
+#define VBE_VESA_MODE_1024X768X565 0x117
+#define VBE_VESA_MODE_1024X768X888 0x118
+#define VBE_VESA_MODE_1280X1024X1555 0x119
+#define VBE_VESA_MODE_1280X1024X565 0x11A
+#define VBE_VESA_MODE_1280X1024X888 0x11B
+#define VBE_VESA_MODE_1600X1200X8 0x11C
+#define VBE_VESA_MODE_1600X1200X1555 0x11D
+#define VBE_VESA_MODE_1600X1200X565 0x11E
+#define VBE_VESA_MODE_1600X1200X888 0x11F
+
+// BOCHS/PLEX86 'own' mode numbers
+#define VBE_OWN_MODE_320X200X8888 0x140
+#define VBE_OWN_MODE_640X400X8888 0x141
+#define VBE_OWN_MODE_640X480X8888 0x142
+#define VBE_OWN_MODE_800X600X8888 0x143
+#define VBE_OWN_MODE_1024X768X8888 0x144
+#define VBE_OWN_MODE_1280X1024X8888 0x145
+#define VBE_OWN_MODE_320X200X8 0x146
+#define VBE_OWN_MODE_1600X1200X8888 0x147
+#define VBE_OWN_MODE_1152X864X8 0x148
+#define VBE_OWN_MODE_1152X864X1555 0x149
+#define VBE_OWN_MODE_1152X864X565 0x14a
+#define VBE_OWN_MODE_1152X864X888 0x14b
+#define VBE_OWN_MODE_1152X864X8888 0x14c
+
+#define VBE_VESA_MODE_END_OF_LIST 0xFFFF
+
+// Capabilities
+
+#define VBE_CAPABILITY_8BIT_DAC 0x0001
+#define VBE_CAPABILITY_NOT_VGA_COMPATIBLE 0x0002
+#define VBE_CAPABILITY_RAMDAC_USE_BLANK_BIT 0x0004
+#define VBE_CAPABILITY_STEREOSCOPIC_SUPPORT 0x0008
+#define VBE_CAPABILITY_STEREO_VIA_VESA_EVC 0x0010
+
+// Mode Attributes
+
+#define VBE_MODE_ATTRIBUTE_SUPPORTED 0x0001
+#define VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE 0x0002
+#define VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT 0x0004
+#define VBE_MODE_ATTRIBUTE_COLOR_MODE 0x0008
+#define VBE_MODE_ATTRIBUTE_GRAPHICS_MODE 0x0010
+#define VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE 0x0020
+#define VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW 0x0040
+#define VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE 0x0080
+#define VBE_MODE_ATTRIBUTE_DOUBLE_SCAN_MODE 0x0100
+#define VBE_MODE_ATTRIBUTE_INTERLACE_MODE 0x0200
+#define VBE_MODE_ATTRIBUTE_HARDWARE_TRIPLE_BUFFER 0x0400
+#define VBE_MODE_ATTRIBUTE_HARDWARE_STEREOSCOPIC_DISPLAY 0x0800
+#define VBE_MODE_ATTRIBUTE_DUAL_DISPLAY_START_ADDRESS 0x1000
+
+#define VBE_MODE_ATTTRIBUTE_LFB_ONLY ( VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW | VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE )
+
+// Window attributes
+
+#define VBE_WINDOW_ATTRIBUTE_RELOCATABLE 0x01
+#define VBE_WINDOW_ATTRIBUTE_READABLE 0x02
+#define VBE_WINDOW_ATTRIBUTE_WRITEABLE 0x04
+
+// Memory model
+
+#define VBE_MEMORYMODEL_TEXT_MODE 0x00
+#define VBE_MEMORYMODEL_CGA_GRAPHICS 0x01
+#define VBE_MEMORYMODEL_HERCULES_GRAPHICS 0x02
+#define VBE_MEMORYMODEL_PLANAR 0x03
+#define VBE_MEMORYMODEL_PACKED_PIXEL 0x04
+#define VBE_MEMORYMODEL_NON_CHAIN_4_256 0x05
+#define VBE_MEMORYMODEL_DIRECT_COLOR 0x06
+#define VBE_MEMORYMODEL_YUV 0x07
+
+// DirectColorModeInfo
+
+#define VBE_DIRECTCOLOR_COLOR_RAMP_PROGRAMMABLE 0x01
+#define VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE 0x02
+
+// GUEST <-> HOST Communication API
+
+// FIXME: either dynamicly ask host for this or put somewhere high in physical memory
+// like 0xE0000000
+
+
+ #define VBE_DISPI_BANK_ADDRESS 0xA0000
+ #define VBE_DISPI_BANK_SIZE_KB 64
+
+ #define VBE_DISPI_MAX_XRES 1024
+ #define VBE_DISPI_MAX_YRES 768
+
+ #define VBE_DISPI_IOPORT_INDEX 0x01CE
+ #define VBE_DISPI_IOPORT_DATA 0x01CF
+
+ #define VBE_DISPI_INDEX_ID 0x0
+ #define VBE_DISPI_INDEX_XRES 0x1
+ #define VBE_DISPI_INDEX_YRES 0x2
+ #define VBE_DISPI_INDEX_BPP 0x3
+ #define VBE_DISPI_INDEX_ENABLE 0x4
+ #define VBE_DISPI_INDEX_BANK 0x5
+ #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+ #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+ #define VBE_DISPI_INDEX_X_OFFSET 0x8
+ #define VBE_DISPI_INDEX_Y_OFFSET 0x9
+
+ #define VBE_DISPI_ID0 0xB0C0
+ #define VBE_DISPI_ID1 0xB0C1
+ #define VBE_DISPI_ID2 0xB0C2
+ #define VBE_DISPI_ID3 0xB0C3
+ #define VBE_DISPI_ID4 0xB0C4
+
+ #define VBE_DISPI_DISABLED 0x00
+ #define VBE_DISPI_ENABLED 0x01
+ #define VBE_DISPI_GETCAPS 0x02
+ #define VBE_DISPI_8BIT_DAC 0x20
+ #define VBE_DISPI_LFB_ENABLED 0x40
+ #define VBE_DISPI_NOCLEARMEM 0x80
+
+ #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+#endif
diff --git a/kvm/vgabios/vbe_display_api.txt b/kvm/vgabios/vbe_display_api.txt
new file mode 100644
index 000000000..fddb78b4b
--- /dev/null
+++ b/kvm/vgabios/vbe_display_api.txt
@@ -0,0 +1,237 @@
+VBE Display API
+-------------------------------------------------------------------------------------------------------------
+ This document is part of the Bochs/VBEBios documentation,
+ it specifies the bochs host <-> vbebios client communication.
+
+ That means, the display code implementation and the vbebios code depend
+ very heavily on each other. As such, this documents needs be synchronised
+ between bochs CVS and the vgabios CVS.
+
+ This document does not describe how the VBEBios implements the VBE2/3 spec.
+ This document does not describe how the Bochs display code will display gfx based upon this spec.
+
+
+API History
+-----------
+0xb0c0 supports the following VBE_DISPI_ interfaces (present in Bochs 1.4):
+ VBE_DISPI_INDEX_ID
+ VBE_DISPI_INDEX_XRES
+ VBE_DISPI_INDEX_YRES
+ VBE_DISPI_INDEX_BPP
+ VBE_DISPI_INDEX_ENABLE
+ VBE_DISPI_INDEX_BANK
+
+ Bpp format supported is:
+ VBE_DISPI_BPP_8
+
+0xb0c1 supports 0xb0c0 VBE_DISPI_ interfaces, additional interfaces (present in Bochs 2.0):
+ VBE_DISPI_INDEX_VIRT_WIDTH
+ VBE_DISPI_INDEX_VIRT_HEIGHT
+ VBE_DISPI_INDEX_X_OFFSET
+ VBE_DISPI_INDEX_Y_OFFSET
+
+0xb0c2 supports 0xb0c1 VBE_DISPI_ interfaces, interfaces updated for
+ additional features (present in Bochs 2.1):
+ VBE_DISPI_INDEX_BPP supports >8bpp color depth (value = bits)
+ VBE_DISPI_INDEX_ENABLE supports new flags VBE_DISPI_NOCLEARMEM and VBE_DISPI_LFB_ENABLED
+ VBE i/o registers changed from 0xFF80/81 to 0x01CE/CF
+
+0xb0c3 supports 0xb0c2 VBE_DISPI_ interfaces, interfaces updated for
+ additional features:
+ VBE_DISPI_INDEX_ENABLE supports new flags VBE_DISPI_GETCAPS and VBE_DISPI_8BIT_DAC
+
+0xb0c4 VBE video memory increased to 8 MB
+
+
+History
+-------
+ Version 0.6 2002 Nov 23 Jeroen Janssen
+ - Added LFB support
+ - Added Virt width, height and x,y offset
+
+ Version 0.5 2002 March 08 Jeroen Janssen
+ - Added documentation about panic behaviour / current limits of the data values.
+ - Changed BPP API (in order to include future (A)RGB formats)
+ - Initial version (based upon extended display text of the vbe bochs display patch)
+
+
+Todo
+----
+ Version 0.6+ [random order]
+ - Add lots of different (A)RGB formats
+
+References
+----------
+ [VBE3] VBE 3 Specification at
+ http://www.vesa.org/vbe3.pdf
+
+ [BOCHS] Bochs Open Source IA-32 Emulator at
+ http://bochs.sourceforge.net
+
+ [VBEBIOS] VBE Bios for Bochs at
+ http://savannah.gnu.org/projects/vgabios/
+
+ [Screenshots] Screenshots of programs using the VBE Bios at
+ http://japj.org/projects/bochs_plex86/screenshots.html
+
+Abbreviations
+-------------
+ VBE Vesa Bios Extension
+ DISPI (Bochs) Display Interface
+ BPP Bits Per Pixel
+ LFB Linear Frame Buffer
+
+
+#defines
+--------
+vbetables-gen.c
+ #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 8
+
+vbe.h
+ #define VBE_DISPI_BANK_ADDRESS 0xA0000
+ #define VBE_DISPI_BANK_SIZE_KB 64
+
+ #define VBE_DISPI_MAX_XRES 1024
+ #define VBE_DISPI_MAX_YRES 768
+
+ #define VBE_DISPI_IOPORT_INDEX 0x01CE
+ #define VBE_DISPI_IOPORT_DATA 0x01CF
+
+ #define VBE_DISPI_INDEX_ID 0x0
+ #define VBE_DISPI_INDEX_XRES 0x1
+ #define VBE_DISPI_INDEX_YRES 0x2
+ #define VBE_DISPI_INDEX_BPP 0x3
+ #define VBE_DISPI_INDEX_ENABLE 0x4
+ #define VBE_DISPI_INDEX_BANK 0x5
+ #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+ #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+ #define VBE_DISPI_INDEX_X_OFFSET 0x8
+ #define VBE_DISPI_INDEX_Y_OFFSET 0x9
+
+ #define VBE_DISPI_ID0 0xB0C0
+ #define VBE_DISPI_ID1 0xB0C1
+ #define VBE_DISPI_ID2 0xB0C2
+ #define VBE_DISPI_ID3 0xB0C3
+ #define VBE_DISPI_ID4 0xB0C4
+
+ #define VBE_DISPI_DISABLED 0x00
+ #define VBE_DISPI_ENABLED 0x01
+ #define VBE_DISPI_VBE_ENABLED 0x40
+ #define VBE_DISPI_NOCLEARMEM 0x80
+
+ #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+API
+---
+ The display api works by using a index (VBE_DISPI_IOPORT_INDEX) and
+ data (VBE_DISPI_IOPORT_DATA) ioport. One writes the index of the parameter to the index port.
+ Next, the parameter value can be read or written.
+
+[0xb0c0]
+ * VBE_DISPI_INDEX_ID : WORD {R,W}
+ This parameter can be used to detect the current display API (both bochs & vbebios).
+ The bios writes VBE_DISPI_ID0 to the dataport and reads it back again.
+ This way, the display code knows the vbebios 'ID' and the vbebios can check if the correct
+ display code is present.
+ As a result, a PANIC can be generated if an incompatible vbebios/display code combination is detected.
+ This panic can be generated from the bochs display code (NOT the bios, see Notes).
+
+ Example values: VBE_DISPI_ID0
+
+ * VBE_DISPI_INDEX_XRES : WORD {R,W}
+ This parameter can be used to read/write the vbe display X resolution (in pixels).
+ It's illegal to set the XRES when the VBE is enabled (display code should generate PANIC).
+
+ If the value written exceeds VBE_DISPI_MAX_XRES, the display code needs to generate a PANIC.
+
+ Example values: 320,640,800,1024
+
+ * VBE_DISPI_INDEX_YRES : WORD {R,W}
+ This parameter can be used to read/write the vbe display Y resolution (in pixels).
+ It's illegal to set the YRES when the VBE is enabled (display code should generate PANIC).
+
+ If the value written exceeds VBE_DISPI_MAX_YRES, the display code needs to generate a PANIC.
+
+ Example values: 200,400,480,600,768
+
+ * VBE_DISPI_INDEX_BPP : WORD {R,W}
+ This parameter can be used to read/write the vbe display BPP.
+ It's illegal to set the BPP when the VBE is enabled (display code should generate PANIC).
+
+ If the value written is an incompatible BPP, the display code needs to generate a PANIC.
+
+ Example values: VBE_DISPI_BPP_8
+
+ * VBE_DISPI_INDEX_ENABLE : WORD {R,W}
+ This parameter can be used to read/write the vbe ENABLED state.
+ If the bios writes VBE_DISPI_ENABLED then the display code will setup a hostside display mode
+ with the current XRES, YRES and BPP settings.
+ If the bios write VBE_DISPI_DISABLED then the display code will switch back to normal vga mode behaviour.
+
+ Example values: VBE_DISPI_ENABLED, VBE_DISPI_DISABLED
+
+ * VBE_DISPI_INDEX_BANK : WORD {R,W}
+ This parameter can be used to read/write the current selected BANK (at 0xA0000).
+ This can be used for switching banks in banked mode.
+
+[0xb0c1]
+ * VBE_DISPI_INDEX_VIRT_WIDTH : WORD {R,W}
+ This parameter can be used to read/write the current virtual width.
+ Upon enabling a mode, this will be set to the current xres
+ Setting this field during enabled mode will result in the virtual width to be changed.
+ Value will be adjusted if current setting is not possible.
+
+ * VBE_DISPI_INDEX_VIRT_HEIGHT : WORD {R}
+ This parameter can be read in order to obtain the current virtual height.
+ This setting will be adjusted after setting a virtual width in order to stay within limit of video memory.
+
+ * VBE_DISPI_INDEX_X_OFFSET : WORD {R,W}
+ The current X offset (in pixels!) of the visible screen part.
+ Writing a new offset will also result in a complete screen refresh.
+
+ * VBE_DISPI_INDEX_Y_OFFSET : WORD {R,W}
+ The current Y offset (in pixels!) of the visible screen part.
+ Writing a new offset will also result in a complete screen refresh.
+
+
+[0xb0c2]
+ * VBE_DISPI_INDEX_BPP : WORD {R,W}
+ The value written is now the number of bits per pixel. A value of 0 is treated
+ the same as 8 for backward compatibilty. These values are supported: 8, 15,
+ 16, 24 and 32. The value of 4 is not yet handled in the VBE code.
+ * VBE_DISPI_INDEX_ENABLE : WORD {R,W}
+ The new flag VBE_DISPI_NOCLEARMEM allows to preserve the VBE video memory.
+ The new flag VBE_DISPI_LFB_ENABLED indicates the usage of the LFB.
+
+[0xb0c3]
+ * VBE_DISPI_INDEX_ENABLE : WORD {R,W}
+ If the new flag VBE_DISPI_GETCAPS is enabled, the xres, yres and bpp registers
+ return the gui capabilities.
+ The new flag VBE_DISPI_8BIT_DAC switches the DAC to 8 bit mode.
+
+[0xb0c4]
+ * VBE_DISPI_TOTAL_VIDEO_MEMORY_MB set to 8 (moved to auto-generated vbetables.h)
+
+Displaying GFX (banked mode)
+--------------
+ What happens is that the total screen is devided in banks of 'VBE_DISPI_BANK_SIZE_KB' KiloByte in size.
+ If you want to set a pixel you can calculate its bank by doing:
+
+ offset = pixel_x + pixel_y * resolution_x;
+ bank = offset / 64 Kb (rounded 1.9999 -> 1)
+
+ bank_pixel_pos = offset - bank * 64Kb
+
+ Now you can set the current bank and put the pixel at VBE_DISPI_BANK_ADDRESS + bank_pixel_pos
+
+Displaying GFX (linear frame buffer mode)
+--------------
+ NOT WRITTEN YET
+
+Notes
+-----
+ * Since the XRES/YRES/BPP may not be written when VBE is enabled, if you want to switch from one VBE mode
+ to another, you will need to disable VBE first.
+
+ * Note when the bios doesn't find a valid DISPI_ID, it can disable the VBE functions. This allows people to
+ use the same bios for both vbe enabled and disabled bochs executables.
diff --git a/kvm/vgabios/vbetables-gen.c b/kvm/vgabios/vbetables-gen.c
new file mode 100644
index 000000000..3bf979d3d
--- /dev/null
+++ b/kvm/vgabios/vbetables-gen.c
@@ -0,0 +1,264 @@
+/* Generate the VGABIOS VBE Tables */
+#include <stdlib.h>
+#include <stdio.h>
+
+#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 16
+
+typedef struct {
+ int width;
+ int height;
+ int depth;
+ int mode;
+} ModeInfo;
+
+ModeInfo modes[] = {
+ /* standard VESA modes */
+{ 640, 400, 8 , 0x100},
+{ 640, 480, 8 , 0x101},
+{ 800, 600, 4 , 0x102},
+{ 800, 600, 8 , 0x103},
+{ 1024, 768, 4 , 0x104},
+{ 1024, 768, 8 , 0x105},
+{ 1280, 1024, 4 , 0x106},
+{ 1280, 1024, 8 , 0x107},
+{ 320, 200, 15 , 0x10D},
+{ 320, 200, 16 , 0x10E},
+{ 320, 200, 24 , 0x10F},
+{ 640, 480, 15 , 0x110},
+{ 640, 480, 16 , 0x111},
+{ 640, 480, 24 , 0x112},
+{ 800, 600, 15 , 0x113},
+{ 800, 600, 16 , 0x114},
+{ 800, 600, 24 , 0x115},
+{ 1024, 768, 15 , 0x116},
+{ 1024, 768, 16 , 0x117},
+{ 1024, 768, 24 , 0x118},
+{ 1280, 1024, 15 , 0x119},
+{ 1280, 1024, 16 , 0x11A},
+{ 1280, 1024, 24 , 0x11B},
+{ 1600, 1200, 8 , 0x11C},
+{ 1600, 1200, 15 , 0x11D},
+{ 1600, 1200, 16 , 0x11E},
+{ 1600, 1200, 24 , 0x11F},
+
+ /* BOCHS/PLE, 86 'own' mode numbers */
+{ 320, 200, 32 , 0x140},
+{ 640, 400, 32 , 0x141},
+{ 640, 480, 32 , 0x142},
+{ 800, 600, 32 , 0x143},
+{ 1024, 768, 32 , 0x144},
+{ 1280, 1024, 32 , 0x145},
+{ 320, 200, 8 , 0x146},
+{ 1600, 1200, 32 , 0x147},
+{ 1152, 864, 8 , 0x148},
+{ 1152, 864, 15 , 0x149},
+{ 1152, 864, 16 , 0x14a},
+{ 1152, 864, 24 , 0x14b},
+{ 1152, 864, 32 , 0x14c},
+{ 1280, 768, 16 , 0x175},
+{ 1280, 768, 24 , 0x176},
+{ 1280, 768, 32 , 0x177},
+{ 1280, 800, 16 , 0x178},
+{ 1280, 800, 24 , 0x179},
+{ 1280, 800, 32 , 0x17a},
+{ 1280, 960, 16 , 0x17b},
+{ 1280, 960, 24 , 0x17c},
+{ 1280, 960, 32 , 0x17d},
+{ 1440, 900, 16 , 0x17e},
+{ 1440, 900, 24 , 0x17f},
+{ 1440, 900, 32 , 0x180},
+{ 1400, 1050, 16 , 0x181},
+{ 1400, 1050, 24 , 0x182},
+{ 1400, 1050, 32 , 0x183},
+{ 1680, 1050, 16 , 0x184},
+{ 1680, 1050, 24 , 0x185},
+{ 1680, 1050, 32 , 0x186},
+{ 1920, 1200, 16 , 0x187},
+{ 1920, 1200, 24 , 0x188},
+{ 1920, 1200, 32 , 0x189},
+{ 2560, 1600, 16 , 0x18a},
+{ 2560, 1600, 24 , 0x18b},
+{ 2560, 1600, 32 , 0x18c},
+{ 0, },
+};
+
+int main(int argc, char **argv)
+{
+ const ModeInfo *pm;
+ int pages, pitch;
+ int r_size, r_pos, g_size, g_pos, b_size, b_pos, a_size, a_pos;
+ const char *str;
+ long vram_size = VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024 * 1024;
+
+ printf("/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n");
+ printf("#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB %d\n\n", VBE_DISPI_TOTAL_VIDEO_MEMORY_MB);
+ printf("static ModeInfoListItem mode_info_list[]=\n");
+ printf("{\n");
+ for (pm = modes; pm->mode != 0; pm++) {
+ if (pm->depth == 4)
+ pitch = (pm->width + 7) / 8;
+ else
+ pitch = pm->width * ((pm->depth + 7) / 8);
+ pages = vram_size / (pm->height * pitch);
+ if (pages > 0) {
+ printf("{ 0x%04x, /* %dx%dx%d */\n",
+ pm->mode, pm->width, pm->height, pm->depth);
+ if (pm->depth == 4)
+ printf("{ /*Bit16u ModeAttributes*/ %s,\n",
+ "VBE_MODE_ATTRIBUTE_SUPPORTED | "
+ "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | "
+ "VBE_MODE_ATTRIBUTE_COLOR_MODE | "
+ "VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT | "
+ "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE");
+ else
+ printf("{ /*Bit16u ModeAttributes*/ %s,\n",
+ "VBE_MODE_ATTRIBUTE_SUPPORTED | "
+ "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | "
+ "VBE_MODE_ATTRIBUTE_COLOR_MODE | "
+ "VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE | "
+ "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE");
+ printf("/*Bit8u WinAAttributes*/ %s,\n",
+ "VBE_WINDOW_ATTRIBUTE_RELOCATABLE | "
+ "VBE_WINDOW_ATTRIBUTE_READABLE | "
+ "VBE_WINDOW_ATTRIBUTE_WRITEABLE");
+
+ printf("/*Bit8u WinBAttributes*/ %d,\n", 0);
+
+ printf("/*Bit16u WinGranularity*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB");
+
+ printf("/*Bit16u WinSize*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB");
+
+ printf("/*Bit16u WinASegment*/ %s,\n", "VGAMEM_GRAPH");
+
+ printf("/*Bit16u WinBSegment*/ 0x%04x,\n", 0);
+
+ printf("/*Bit32u WinFuncPtr*/ %d,\n", 0);
+
+ printf("/*Bit16u BytesPerScanLine*/ %d,\n", pitch);
+
+ // Mandatory information for VBE 1.2 and above
+ printf("/*Bit16u XResolution*/ %d,\n", pm->width);
+ printf("/*Bit16u YResolution*/ %d,\n", pm->height);
+ printf("/*Bit8u XCharSize*/ %d,\n", 8);
+ printf("/*Bit8u YCharSize*/ %d,\n", 16);
+ if (pm->depth == 4) {
+ printf("/*Bit8u NumberOfPlanes*/ %d,\n", 4);
+ } else {
+ printf("/*Bit8u NumberOfPlanes*/ %d,\n", 1);
+ }
+ printf("/*Bit8u BitsPerPixel*/ %d,\n", pm->depth);
+ printf("/*Bit8u NumberOfBanks*/ %d,\n",
+ (pm->height * pitch + 65535) / 65536);
+
+ if (pm->depth == 4)
+ str = "VBE_MEMORYMODEL_PLANAR";
+ else if (pm->depth == 8)
+ str = "VBE_MEMORYMODEL_PACKED_PIXEL";
+ else
+ str = "VBE_MEMORYMODEL_DIRECT_COLOR";
+ printf("/*Bit8u MemoryModel*/ %s,\n", str);
+ printf("/*Bit8u BankSize*/ %d,\n", 0);
+ if (pm->depth == 4)
+ printf("/*Bit8u NumberOfImagePages*/ %d,\n", (pages / 4) - 1);
+ else
+ printf("/*Bit8u NumberOfImagePages*/ %d,\n", pages - 1);
+ printf("/*Bit8u Reserved_page*/ %d,\n", 0);
+
+ // Direct Color fields (required for direct/6 and YUV/7 memory models)
+ switch(pm->depth) {
+ case 15:
+ r_size = 5;
+ r_pos = 10;
+ g_size = 5;
+ g_pos = 5;
+ b_size = 5;
+ b_pos = 0;
+ a_size = 1;
+ a_pos = 15;
+ break;
+ case 16:
+ r_size = 5;
+ r_pos = 11;
+ g_size = 6;
+ g_pos = 5;
+ b_size = 5;
+ b_pos = 0;
+ a_size = 0;
+ a_pos = 0;
+ break;
+ case 24:
+ r_size = 8;
+ r_pos = 16;
+ g_size = 8;
+ g_pos = 8;
+ b_size = 8;
+ b_pos = 0;
+ a_size = 0;
+ a_pos = 0;
+ break;
+ case 32:
+ r_size = 8;
+ r_pos = 16;
+ g_size = 8;
+ g_pos = 8;
+ b_size = 8;
+ b_pos = 0;
+ a_size = 8;
+ a_pos = 24;
+ break;
+ default:
+ r_size = 0;
+ r_pos = 0;
+ g_size = 0;
+ g_pos = 0;
+ b_size = 0;
+ b_pos = 0;
+ a_size = 0;
+ a_pos = 0;
+ break;
+ }
+
+ printf("/*Bit8u RedMaskSize*/ %d,\n", r_size);
+ printf("/*Bit8u RedFieldPosition*/ %d,\n", r_pos);
+ printf("/*Bit8u GreenMaskSize*/ %d,\n", g_size);
+ printf("/*Bit8u GreenFieldPosition*/ %d,\n", g_pos);
+ printf("/*Bit8u BlueMaskSize*/ %d,\n", b_size);
+ printf("/*Bit8u BlueFieldPosition*/ %d,\n", b_pos);
+ printf("/*Bit8u RsvdMaskSize*/ %d,\n", a_size);
+ printf("/*Bit8u RsvdFieldPosition*/ %d,\n", a_pos);
+ if (pm->depth == 32)
+ printf("/*Bit8u DirectColorModeInfo*/ %s,\n",
+ "VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE");
+ else
+ printf("/*Bit8u DirectColorModeInfo*/ %s,\n", "0");
+
+// Mandatory information for VBE 2.0 and above
+ if (pm->depth > 4)
+ printf("/*Bit32u PhysBasePtr*/ %s,\n",
+ "VBE_DISPI_LFB_PHYSICAL_ADDRESS");
+ else
+ printf("/*Bit32u PhysBasePtr*/ %s,\n", "0");
+ printf("/*Bit32u OffScreenMemOffset*/ %d,\n", 0);
+ printf("/*Bit16u OffScreenMemSize*/ %d,\n", 0);
+ // Mandatory information for VBE 3.0 and above
+ printf("/*Bit16u LinBytesPerScanLine*/ %d,\n", pitch);
+ printf("/*Bit8u BnkNumberOfPages*/ %d,\n", 0);
+ printf("/*Bit8u LinNumberOfPages*/ %d,\n", 0);
+ printf("/*Bit8u LinRedMaskSize*/ %d,\n", r_size);
+ printf("/*Bit8u LinRedFieldPosition*/ %d,\n", r_pos);
+ printf("/*Bit8u LinGreenMaskSize*/ %d,\n", g_size);
+ printf("/*Bit8u LinGreenFieldPosition*/ %d,\n", g_pos);
+ printf("/*Bit8u LinBlueMaskSize*/ %d,\n", b_size);
+ printf("/*Bit8u LinBlueFieldPosition*/ %d,\n", b_pos);
+ printf("/*Bit8u LinRsvdMaskSize*/ %d,\n", a_size);
+ printf("/*Bit8u LinRsvdFieldPosition*/ %d,\n", a_pos);
+ printf("/*Bit32u MaxPixelClock*/ %d,\n", 0);
+ printf("} },\n");
+ }
+ }
+ printf("{ VBE_VESA_MODE_END_OF_LIST,\n");
+ printf("{ 0,\n");
+ printf("} },\n");
+ printf("};\n");
+ return 0;
+}
diff --git a/kvm/vgabios/vgabios.c b/kvm/vgabios/vgabios.c
new file mode 100644
index 000000000..e6fe2a0d0
--- /dev/null
+++ b/kvm/vgabios/vgabios.c
@@ -0,0 +1,3853 @@
+// ============================================================================================
+/*
+ * vgabios.c
+ */
+// ============================================================================================
+//
+// Copyright (C) 2001-2008 the LGPL VGABios developers Team
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// ============================================================================================
+//
+// This VGA Bios is specific to the plex86/bochs Emulated VGA card.
+// You can NOT drive any physical vga card with it.
+//
+// ============================================================================================
+//
+// This file contains code ripped from :
+// - rombios.c of plex86
+//
+// This VGA Bios contains fonts from :
+// - fntcol16.zip (c) by Joseph Gil avalable at :
+// ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+// These fonts are public domain
+//
+// This VGA Bios is based on information taken from :
+// - Kevin Lawton's vga card emulation for bochs/plex86
+// - Ralf Brown's interrupts list available at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
+// - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/
+// - Michael Abrash's Graphics Programming Black Book
+// - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex
+// - DOSEMU 1.0.1 source code for several tables values and formulas
+//
+// Thanks for patches, comments and ideas to :
+// - techt@pikeonline.net
+//
+// ============================================================================================
+
+#include "vgabios.h"
+
+#ifdef VBE
+#include "vbe.h"
+#endif
+
+#define USE_BX_INFO
+
+/* Declares */
+static Bit8u read_byte();
+static Bit16u read_word();
+static void write_byte();
+static void write_word();
+static Bit8u inb();
+static Bit16u inw();
+static void outb();
+static void outw();
+
+static Bit16u get_SS();
+
+// Output
+static void printf();
+static void unimplemented();
+static void unknown();
+
+static Bit8u find_vga_entry();
+
+static void memsetb();
+static void memsetw();
+static void memcpyb();
+static void memcpyw();
+
+static void biosfn_set_video_mode();
+static void biosfn_set_cursor_shape();
+static void biosfn_set_cursor_pos();
+static void biosfn_get_cursor_pos();
+static void biosfn_set_active_page();
+static void biosfn_scroll();
+static void biosfn_read_char_attr();
+static void biosfn_write_char_attr();
+static void biosfn_write_char_only();
+static void biosfn_write_pixel();
+static void biosfn_read_pixel();
+static void biosfn_write_teletype();
+static void biosfn_perform_gray_scale_summing();
+static void biosfn_load_text_user_pat();
+static void biosfn_load_text_8_14_pat();
+static void biosfn_load_text_8_8_pat();
+static void biosfn_load_text_8_16_pat();
+static void biosfn_load_gfx_8_8_chars();
+static void biosfn_load_gfx_user_chars();
+static void biosfn_load_gfx_8_14_chars();
+static void biosfn_load_gfx_8_8_dd_chars();
+static void biosfn_load_gfx_8_16_chars();
+static void biosfn_get_font_info();
+static void biosfn_alternate_prtsc();
+static void biosfn_switch_video_interface();
+static void biosfn_enable_video_refresh_control();
+static void biosfn_write_string();
+static void biosfn_read_state_info();
+static void biosfn_read_video_state_size();
+static Bit16u biosfn_save_video_state();
+static Bit16u biosfn_restore_video_state();
+extern Bit8u video_save_pointer_table[];
+
+// This is for compiling with gcc2 and gcc3
+#define ASM_START #asm
+#define ASM_END #endasm
+
+ASM_START
+
+MACRO SET_INT_VECTOR
+ push ds
+ xor ax, ax
+ mov ds, ax
+ mov ax, ?3
+ mov ?1*4, ax
+ mov ax, ?2
+ mov ?1*4+2, ax
+ pop ds
+MEND
+
+ASM_END
+
+ASM_START
+.text
+.rom
+.org 0
+
+use16 386
+
+vgabios_start:
+.byte 0x55, 0xaa /* BIOS signature, required for BIOS extensions */
+
+.byte 0x40 /* BIOS extension length in units of 512 bytes */
+
+
+vgabios_entry_point:
+
+ jmp vgabios_init_func
+
+#ifdef PCIBIOS
+.org 0x18
+.word vgabios_pci_data
+#endif
+
+// Info from Bart Oldeman
+.org 0x1e
+.ascii "IBM"
+.byte 0x00
+
+vgabios_name:
+.ascii "Plex86/Bochs VGABios"
+#ifdef PCIBIOS
+.ascii " (PCI)"
+#endif
+.ascii " "
+.byte 0x00
+
+vgabios_version:
+#ifndef VGABIOS_VERS
+.ascii "current-cvs"
+#else
+.ascii VGABIOS_VERS
+#endif
+.ascii " "
+
+vgabios_date:
+.ascii VGABIOS_DATE
+.byte 0x0a,0x0d
+.byte 0x00
+
+vgabios_copyright:
+.ascii "(C) 2008 the LGPL VGABios developers Team"
+.byte 0x0a,0x0d
+.byte 0x00
+
+vgabios_license:
+.ascii "This VGA/VBE Bios is released under the GNU LGPL"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+vgabios_website:
+.ascii "Please visit :"
+.byte 0x0a,0x0d
+;;.ascii " . http://www.plex86.org"
+;;.byte 0x0a,0x0d
+.ascii " . http://bochs.sourceforge.net"
+.byte 0x0a,0x0d
+.ascii " . http://www.nongnu.org/vgabios"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+#ifdef PCIBIOS
+vgabios_pci_data:
+.ascii "PCIR"
+#ifdef CIRRUS
+.word 0x1013
+.word 0x00b8 // CLGD5446
+#else
+#error "Unknown PCI vendor and device id"
+#endif
+.word 0 // reserved
+.word 0x18 // dlen
+.byte 0 // revision
+.byte 0x0 // class,hi: vga display
+.word 0x300 // class,lo: vga display
+.word 0x40 // bios size
+.word 1 // revision
+.byte 0 // intel x86 data
+.byte 0x80 // last image
+.word 0 // reserved
+#endif
+
+
+;; ============================================================================================
+;;
+;; Init Entry point
+;;
+;; ============================================================================================
+vgabios_init_func:
+
+;; init vga card
+ call init_vga_card
+
+;; init basic bios vars
+ call init_bios_area
+
+#ifdef VBE
+;; init vbe functions
+ call vbe_init
+#endif
+
+;; set int10 vect
+ SET_INT_VECTOR(0x10, #0xC000, #vgabios_int10_handler)
+
+#ifdef CIRRUS
+ call cirrus_init
+#endif
+
+;; display splash screen
+ call _display_splash_screen
+
+;; init video mode and clear the screen
+ mov ax,#0x0003
+ int #0x10
+
+;; show info
+ call _display_info
+
+#ifdef VBE
+;; show vbe info
+ call vbe_display_info
+#endif
+
+#ifdef CIRRUS
+;; show cirrus info
+ call cirrus_display_info
+#endif
+
+ retf
+ASM_END
+
+/*
+ * int10 handled here
+ */
+ASM_START
+vgabios_int10_handler:
+ pushf
+#ifdef DEBUG
+ push es
+ push ds
+ pusha
+ mov bx, #0xc000
+ mov ds, bx
+ call _int10_debugmsg
+ popa
+ pop ds
+ pop es
+#endif
+ cmp ah, #0x0f
+ jne int10_test_1A
+ call biosfn_get_video_mode
+ jmp int10_end
+int10_test_1A:
+ cmp ah, #0x1a
+ jne int10_test_0B
+ call biosfn_group_1A
+ jmp int10_end
+int10_test_0B:
+ cmp ah, #0x0b
+ jne int10_test_1103
+ call biosfn_group_0B
+ jmp int10_end
+int10_test_1103:
+ cmp ax, #0x1103
+ jne int10_test_12
+ call biosfn_set_text_block_specifier
+ jmp int10_end
+int10_test_12:
+ cmp ah, #0x12
+ jne int10_test_101B
+ cmp bl, #0x10
+ jne int10_test_BL30
+ call biosfn_get_ega_info
+ jmp int10_end
+int10_test_BL30:
+ cmp bl, #0x30
+ jne int10_test_BL31
+ call biosfn_select_vert_res
+ jmp int10_end
+int10_test_BL31:
+ cmp bl, #0x31
+ jne int10_test_BL32
+ call biosfn_enable_default_palette_loading
+ jmp int10_end
+int10_test_BL32:
+ cmp bl, #0x32
+ jne int10_test_BL33
+ call biosfn_enable_video_addressing
+ jmp int10_end
+int10_test_BL33:
+ cmp bl, #0x33
+ jne int10_test_BL34
+ call biosfn_enable_grayscale_summing
+ jmp int10_end
+int10_test_BL34:
+ cmp bl, #0x34
+ jne int10_normal
+ call biosfn_enable_cursor_emulation
+ jmp int10_end
+int10_test_101B:
+ cmp ax, #0x101b
+ je int10_normal
+ cmp ah, #0x10
+#ifndef VBE
+ jne int10_normal
+#else
+ jne int10_test_4F
+#endif
+ call biosfn_group_10
+ jmp int10_end
+#ifdef VBE
+int10_test_4F:
+ cmp ah, #0x4f
+ jne int10_normal
+ cmp al, #0x03
+ jne int10_test_vbe_05
+ call vbe_biosfn_return_current_mode
+ jmp int10_end
+int10_test_vbe_05:
+ cmp al, #0x05
+ jne int10_test_vbe_06
+ call vbe_biosfn_display_window_control
+ jmp int10_end
+int10_test_vbe_06:
+ cmp al, #0x06
+ jne int10_test_vbe_07
+ call vbe_biosfn_set_get_logical_scan_line_length
+ jmp int10_end
+int10_test_vbe_07:
+ cmp al, #0x07
+ jne int10_test_vbe_08
+ call vbe_biosfn_set_get_display_start
+ jmp int10_end
+int10_test_vbe_08:
+ cmp al, #0x08
+ jne int10_test_vbe_0A
+ call vbe_biosfn_set_get_dac_palette_format
+ jmp int10_end
+int10_test_vbe_0A:
+ cmp al, #0x0A
+ jne int10_normal
+ call vbe_biosfn_return_protected_mode_interface
+ jmp int10_end
+#endif
+
+int10_normal:
+ push es
+ push ds
+ pusha
+
+;; We have to set ds to access the right data segment
+ mov bx, #0xc000
+ mov ds, bx
+ call _int10_func
+
+ popa
+ pop ds
+ pop es
+int10_end:
+ popf
+ iret
+ASM_END
+
+#include "vgatables.h"
+#include "vgafonts.h"
+
+/*
+ * Boot time harware inits
+ */
+ASM_START
+init_vga_card:
+;; switch to color mode and enable CPU access 480 lines
+ mov dx, #0x3C2
+ mov al, #0xC3
+ outb dx,al
+
+;; more than 64k 3C4/04
+ mov dx, #0x3C4
+ mov al, #0x04
+ outb dx,al
+ mov dx, #0x3C5
+ mov al, #0x02
+ outb dx,al
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+ mov bx, #msg_vga_init
+ push bx
+ call _printf
+#endif
+ inc sp
+ inc sp
+ ret
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+msg_vga_init:
+.ascii "VGABios $Id$"
+.byte 0x0d,0x0a,0x00
+#endif
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+/*
+ * Boot time bios area inits
+ */
+ASM_START
+init_bios_area:
+ push ds
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+
+;; init detected hardware BIOS Area
+ mov bx, # BIOSMEM_INITIAL_MODE
+ mov ax, [bx]
+ and ax, #0xffcf
+;; set 80x25 color (not clear from RBIL but usual)
+ or ax, #0x0020
+ mov [bx], ax
+
+;; Just for the first int10 find its children
+
+;; the default char height
+ mov bx, # BIOSMEM_CHAR_HEIGHT
+ mov al, #0x10
+ mov [bx], al
+
+;; Clear the screen
+ mov bx, # BIOSMEM_VIDEO_CTL
+ mov al, #0x60
+ mov [bx], al
+
+;; Set the basic screen we have
+ mov bx, # BIOSMEM_SWITCHES
+ mov al, #0xf9
+ mov [bx], al
+
+;; Set the basic modeset options
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, #0x51
+ mov [bx], al
+
+;; Set the default MSR
+ mov bx, # BIOSMEM_CURRENT_MSR
+ mov al, #0x09
+ mov [bx], al
+
+ pop ds
+ ret
+
+_video_save_pointer_table:
+ .word _video_param_table
+ .word 0xc000
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+/*
+ * Boot time Splash screen
+ */
+static void display_splash_screen()
+{
+}
+
+// --------------------------------------------------------------------------------------------
+/*
+ * Tell who we are
+ */
+
+static void display_info()
+{
+ASM_START
+ mov ax,#0xc000
+ mov ds,ax
+ mov si,#vgabios_name
+ call _display_string
+ mov si,#vgabios_version
+ call _display_string
+
+ ;;mov si,#vgabios_copyright
+ ;;call _display_string
+ ;;mov si,#crlf
+ ;;call _display_string
+
+ mov si,#vgabios_license
+ call _display_string
+ mov si,#vgabios_website
+ call _display_string
+ASM_END
+}
+
+static void display_string()
+{
+ // Get length of string
+ASM_START
+ mov ax,ds
+ mov es,ax
+ mov di,si
+ xor cx,cx
+ not cx
+ xor al,al
+ cld
+ repne
+ scasb
+ not cx
+ dec cx
+ push cx
+
+ mov ax,#0x0300
+ mov bx,#0x0000
+ int #0x10
+
+ pop cx
+ mov ax,#0x1301
+ mov bx,#0x000b
+ mov bp,si
+ int #0x10
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+#ifdef DEBUG
+static void int10_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+ Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+ // 0E is write char...
+ if(GET_AH()!=0x0E)
+ printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX);
+}
+#endif
+
+// --------------------------------------------------------------------------------------------
+/*
+ * int10 main dispatcher
+ */
+static void int10_func(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+ Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+
+ // BIOS functions
+ switch(GET_AH())
+ {
+ case 0x00:
+ biosfn_set_video_mode(GET_AL());
+ switch(GET_AL()&0x7F)
+ {case 6:
+ SET_AL(0x3F);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 7:
+ SET_AL(0x30);
+ break;
+ default:
+ SET_AL(0x20);
+ }
+ break;
+ case 0x01:
+ biosfn_set_cursor_shape(GET_CH(),GET_CL());
+ break;
+ case 0x02:
+ biosfn_set_cursor_pos(GET_BH(),DX);
+ break;
+ case 0x03:
+ biosfn_get_cursor_pos(GET_BH(),&CX,&DX);
+ break;
+ case 0x04:
+ // Read light pen pos (unimplemented)
+#ifdef DEBUG
+ unimplemented();
+#endif
+ AX=0x00;
+ BX=0x00;
+ CX=0x00;
+ DX=0x00;
+ break;
+ case 0x05:
+ biosfn_set_active_page(GET_AL());
+ break;
+ case 0x06:
+ biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_UP);
+ break;
+ case 0x07:
+ biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_DOWN);
+ break;
+ case 0x08:
+ biosfn_read_char_attr(GET_BH(),&AX);
+ break;
+ case 0x09:
+ biosfn_write_char_attr(GET_AL(),GET_BH(),GET_BL(),CX);
+ break;
+ case 0x0A:
+ biosfn_write_char_only(GET_AL(),GET_BH(),GET_BL(),CX);
+ break;
+ case 0x0C:
+ biosfn_write_pixel(GET_BH(),GET_AL(),CX,DX);
+ break;
+ case 0x0D:
+ biosfn_read_pixel(GET_BH(),CX,DX,&AX);
+ break;
+ case 0x0E:
+ // Ralf Brown Interrupt list is WRONG on bh(page)
+ // We do output only on the current page !
+ biosfn_write_teletype(GET_AL(),0xff,GET_BL(),NO_ATTR);
+ break;
+ case 0x10:
+ // All other functions of group AH=0x10 rewritten in assembler
+ biosfn_perform_gray_scale_summing(BX,CX);
+ break;
+ case 0x11:
+ switch(GET_AL())
+ {
+ case 0x00:
+ case 0x10:
+ biosfn_load_text_user_pat(GET_AL(),ES,BP,CX,DX,GET_BL(),GET_BH());
+ break;
+ case 0x01:
+ case 0x11:
+ biosfn_load_text_8_14_pat(GET_AL(),GET_BL());
+ break;
+ case 0x02:
+ case 0x12:
+ biosfn_load_text_8_8_pat(GET_AL(),GET_BL());
+ break;
+ case 0x04:
+ case 0x14:
+ biosfn_load_text_8_16_pat(GET_AL(),GET_BL());
+ break;
+ case 0x20:
+ biosfn_load_gfx_8_8_chars(ES,BP);
+ break;
+ case 0x21:
+ biosfn_load_gfx_user_chars(ES,BP,CX,GET_BL(),GET_DL());
+ break;
+ case 0x22:
+ biosfn_load_gfx_8_14_chars(GET_BL());
+ break;
+ case 0x23:
+ biosfn_load_gfx_8_8_dd_chars(GET_BL());
+ break;
+ case 0x24:
+ biosfn_load_gfx_8_16_chars(GET_BL());
+ break;
+ case 0x30:
+ biosfn_get_font_info(GET_BH(),&ES,&BP,&CX,&DX);
+ break;
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+
+ break;
+ case 0x12:
+ switch(GET_BL())
+ {
+ case 0x20:
+ biosfn_alternate_prtsc();
+ break;
+ case 0x35:
+ biosfn_switch_video_interface(GET_AL(),ES,DX);
+ SET_AL(0x12);
+ break;
+ case 0x36:
+ biosfn_enable_video_refresh_control(GET_AL());
+ SET_AL(0x12);
+ break;
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+ break;
+ case 0x13:
+ biosfn_write_string(GET_AL(),GET_BH(),GET_BL(),CX,GET_DH(),GET_DL(),ES,BP);
+ break;
+ case 0x1B:
+ biosfn_read_state_info(BX,ES,DI);
+ SET_AL(0x1B);
+ break;
+ case 0x1C:
+ switch(GET_AL())
+ {
+ case 0x00:
+ biosfn_read_video_state_size(CX,&BX);
+ break;
+ case 0x01:
+ biosfn_save_video_state(CX,ES,BX);
+ break;
+ case 0x02:
+ biosfn_restore_video_state(CX,ES,BX);
+ break;
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+ SET_AL(0x1C);
+ break;
+
+#ifdef VBE
+ case 0x4f:
+ if (vbe_has_vbe_display()) {
+ switch(GET_AL())
+ {
+ case 0x00:
+ vbe_biosfn_return_controller_information(&AX,ES,DI);
+ break;
+ case 0x01:
+ vbe_biosfn_return_mode_information(&AX,CX,ES,DI);
+ break;
+ case 0x02:
+ vbe_biosfn_set_mode(&AX,BX,ES,DI);
+ break;
+ case 0x04:
+ vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX);
+ break;
+ case 0x09:
+ //FIXME
+#ifdef DEBUG
+ unimplemented();
+#endif
+ // function failed
+ AX=0x100;
+ break;
+ case 0x0A:
+ //FIXME
+#ifdef DEBUG
+ unimplemented();
+#endif
+ // function failed
+ AX=0x100;
+ break;
+ default:
+#ifdef DEBUG
+ unknown();
+#endif
+ // function failed
+ AX=0x100;
+ }
+ }
+ else {
+ // No VBE display
+ AX=0x0100;
+ }
+ break;
+#endif
+
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+}
+
+// ============================================================================================
+//
+// BIOS functions
+//
+// ============================================================================================
+
+static void biosfn_set_video_mode(mode) Bit8u mode;
+{// mode: Bit 7 is 1 if no clear screen
+
+ // Should we clear the screen ?
+ Bit8u noclearmem=mode&0x80;
+ Bit8u line,mmask,*palette,vpti;
+ Bit16u i,twidth,theightm1,cheight;
+ Bit8u modeset_ctl,video_ctl,vga_switches;
+ Bit16u crtc_addr;
+
+#ifdef VBE
+ if (vbe_has_vbe_display()) {
+ dispi_set_enable(VBE_DISPI_DISABLED);
+ }
+#endif // def VBE
+
+ // The real mode
+ mode=mode&0x7f;
+
+ // find the entry in the video modes
+ line=find_vga_entry(mode);
+
+#ifdef DEBUG
+ printf("mode search %02x found line %02x\n",mode,line);
+#endif
+
+ if(line==0xFF)
+ return;
+
+ vpti=line_to_vpti[line];
+ twidth=video_param_table[vpti].twidth;
+ theightm1=video_param_table[vpti].theightm1;
+ cheight=video_param_table[vpti].cheight;
+
+ // Read the bios vga control
+ video_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL);
+
+ // Read the bios vga switches
+ vga_switches=read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES);
+
+ // Read the bios mode set control
+ modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+
+ // Then we know the number of lines
+// FIXME
+
+ // if palette loading (bit 3 of modeset ctl = 0)
+ if((modeset_ctl&0x08)==0)
+ {// Set the PEL mask
+ outb(VGAREG_PEL_MASK,vga_modes[line].pelmask);
+
+ // Set the whole dac always, from 0
+ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+
+ // From which palette
+ switch(vga_modes[line].dacmodel)
+ {case 0:
+ palette=&palette0;
+ break;
+ case 1:
+ palette=&palette1;
+ break;
+ case 2:
+ palette=&palette2;
+ break;
+ case 3:
+ palette=&palette3;
+ break;
+ }
+ // Always 256*3 values
+ for(i=0;i<0x0100;i++)
+ {if(i<=dac_regs[vga_modes[line].dacmodel])
+ {outb(VGAREG_DAC_DATA,palette[(i*3)+0]);
+ outb(VGAREG_DAC_DATA,palette[(i*3)+1]);
+ outb(VGAREG_DAC_DATA,palette[(i*3)+2]);
+ }
+ else
+ {outb(VGAREG_DAC_DATA,0);
+ outb(VGAREG_DAC_DATA,0);
+ outb(VGAREG_DAC_DATA,0);
+ }
+ }
+ if((modeset_ctl&0x02)==0x02)
+ {
+ biosfn_perform_gray_scale_summing(0x00, 0x100);
+ }
+ }
+
+ // Reset Attribute Ctl flip-flop
+ inb(VGAREG_ACTL_RESET);
+
+ // Set Attribute Ctl
+ for(i=0;i<=0x13;i++)
+ {outb(VGAREG_ACTL_ADDRESS,i);
+ outb(VGAREG_ACTL_WRITE_DATA,video_param_table[vpti].actl_regs[i]);
+ }
+ outb(VGAREG_ACTL_ADDRESS,0x14);
+ outb(VGAREG_ACTL_WRITE_DATA,0x00);
+
+ // Set Sequencer Ctl
+ outb(VGAREG_SEQU_ADDRESS,0);
+ outb(VGAREG_SEQU_DATA,0x03);
+ for(i=1;i<=4;i++)
+ {outb(VGAREG_SEQU_ADDRESS,i);
+ outb(VGAREG_SEQU_DATA,video_param_table[vpti].sequ_regs[i - 1]);
+ }
+
+ // Set Grafx Ctl
+ for(i=0;i<=8;i++)
+ {outb(VGAREG_GRDC_ADDRESS,i);
+ outb(VGAREG_GRDC_DATA,video_param_table[vpti].grdc_regs[i]);
+ }
+
+ // Set CRTC address VGA or MDA
+ crtc_addr=vga_modes[line].memmodel==MTEXT?VGAREG_MDA_CRTC_ADDRESS:VGAREG_VGA_CRTC_ADDRESS;
+
+ // Disable CRTC write protection
+ outw(crtc_addr,0x0011);
+ // Set CRTC regs
+ for(i=0;i<=0x18;i++)
+ {outb(crtc_addr,i);
+ outb(crtc_addr+1,video_param_table[vpti].crtc_regs[i]);
+ }
+
+ // Set the misc register
+ outb(VGAREG_WRITE_MISC_OUTPUT,video_param_table[vpti].miscreg);
+
+ // Enable video
+ outb(VGAREG_ACTL_ADDRESS,0x20);
+ inb(VGAREG_ACTL_RESET);
+
+ if(noclearmem==0x00)
+ {
+ if(vga_modes[line].class==TEXT)
+ {
+ memsetw(vga_modes[line].sstart,0,0x0720,0x4000); // 32k
+ }
+ else
+ {
+ if(mode<0x0d)
+ {
+ memsetw(vga_modes[line].sstart,0,0x0000,0x4000); // 32k
+ }
+ else
+ {
+ outb( VGAREG_SEQU_ADDRESS, 0x02 );
+ mmask = inb( VGAREG_SEQU_DATA );
+ outb( VGAREG_SEQU_DATA, 0x0f ); // all planes
+ memsetw(vga_modes[line].sstart,0,0x0000,0x8000); // 64k
+ outb( VGAREG_SEQU_DATA, mmask );
+ }
+ }
+ }
+
+ // Set the BIOS mem
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,mode);
+ write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS,twidth);
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,*(Bit16u *)&video_param_table[vpti].slength_l);
+ write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,crtc_addr);
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theightm1);
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,cheight);
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|noclearmem));
+ write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9);
+ write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x7f);
+
+ // FIXME We nearly have the good tables. to be reworked
+ write_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x08); // 8 is VGA should be ok for now
+ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER, video_save_pointer_table);
+ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2, 0xc000);
+
+ // FIXME
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x00); // Unavailable on vanilla vga, but...
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x00); // Unavailable on vanilla vga, but...
+
+ // Set cursor shape
+ if(vga_modes[line].class==TEXT)
+ {
+ biosfn_set_cursor_shape(0x06,0x07);
+ }
+
+ // Set cursor pos for page 0..7
+ for(i=0;i<8;i++)
+ biosfn_set_cursor_pos(i,0x0000);
+
+ // Set active page 0
+ biosfn_set_active_page(0x00);
+
+ // Write the fonts in memory
+ if(vga_modes[line].class==TEXT)
+ {
+ASM_START
+ ;; copy and activate 8x16 font
+ mov ax, #0x1104
+ mov bl, #0x00
+ int #0x10
+ mov ax, #0x1103
+ mov bl, #0x00
+ int #0x10
+ASM_END
+ }
+
+ // Set the ints 0x1F and 0x43
+ASM_START
+ SET_INT_VECTOR(0x1f, #0xC000, #_vgafont8+128*8)
+ASM_END
+
+ switch(cheight)
+ {case 8:
+ASM_START
+ SET_INT_VECTOR(0x43, #0xC000, #_vgafont8)
+ASM_END
+ break;
+ case 14:
+ASM_START
+ SET_INT_VECTOR(0x43, #0xC000, #_vgafont14)
+ASM_END
+ break;
+ case 16:
+ASM_START
+ SET_INT_VECTOR(0x43, #0xC000, #_vgafont16)
+ASM_END
+ break;
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_cursor_shape (CH,CL)
+Bit8u CH;Bit8u CL;
+{Bit16u cheight,curs,crtc_addr;
+ Bit8u modeset_ctl;
+
+ CH&=0x3f;
+ CL&=0x1f;
+
+ curs=(CH<<8)+CL;
+ write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,curs);
+
+ modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+ cheight = read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ if((modeset_ctl&0x01) && (cheight>8) && (CL<8) && (CH<0x20))
+ {
+ if(CL!=(CH+1))
+ {
+ CH = ((CH+1) * cheight / 8) -1;
+ }
+ else
+ {
+ CH = ((CL+1) * cheight / 8) - 2;
+ }
+ CL = ((CL+1) * cheight / 8) - 1;
+ }
+
+ // CTRC regs 0x0a and 0x0b
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0a);
+ outb(crtc_addr+1,CH);
+ outb(crtc_addr,0x0b);
+ outb(crtc_addr+1,CL);
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_cursor_pos (page, cursor)
+Bit8u page;Bit16u cursor;
+{
+ Bit8u xcurs,ycurs,current;
+ Bit16u nbcols,nbrows,address,crtc_addr;
+
+ // Should not happen...
+ if(page>7)return;
+
+ // Bios cursor pos
+ write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*page, cursor);
+
+ // Set the hardware cursor
+ current=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ if(page==current)
+ {
+ // Get the dimensions
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Calculate the address knowing nbcols nbrows and page num
+ address=SCREEN_IO_START(nbcols,nbrows,page)+xcurs+ycurs*nbcols;
+
+ // CRTC regs 0x0e and 0x0f
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0e);
+ outb(crtc_addr+1,(address&0xff00)>>8);
+ outb(crtc_addr,0x0f);
+ outb(crtc_addr+1,address&0x00ff);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_get_cursor_pos (page,shape, pos)
+Bit8u page;Bit16u *shape;Bit16u *pos;
+{
+ Bit16u ss=get_SS();
+
+ // Default
+ write_word(ss, shape, 0);
+ write_word(ss, pos, 0);
+
+ if(page>7)return;
+ // FIXME should handle VGA 14/16 lines
+ write_word(ss,shape,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE));
+ write_word(ss,pos,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2));
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_active_page (page)
+Bit8u page;
+{
+ Bit16u cursor,dummy,crtc_addr;
+ Bit16u nbcols,nbrows,address;
+ Bit8u mode,line;
+
+ if(page>7)return;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get pos curs pos for the right page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Get the dimensions
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+
+ // Calculate the address knowing nbcols nbrows and page num
+ address=SCREEN_MEM_START(nbcols,nbrows,page);
+ write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START,address);
+
+ // Start address
+ address=SCREEN_IO_START(nbcols,nbrows,page);
+ }
+ else
+ {
+ address = page * (*(Bit16u *)&video_param_table[line_to_vpti[line]].slength_l);
+ }
+
+ // CRTC regs 0x0c and 0x0d
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0c);
+ outb(crtc_addr+1,(address&0xff00)>>8);
+ outb(crtc_addr,0x0d);
+ outb(crtc_addr+1,address&0x00ff);
+
+ // And change the BIOS page
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page);
+
+#ifdef DEBUG
+ printf("Set active page %02x address %04x\n",page,address);
+#endif
+
+ // Display the cursor, now the page is active
+ biosfn_set_cursor_pos(page,cursor);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight)
+Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight;
+{
+ Bit16u src,dest;
+ Bit8u i;
+
+ src=ysrc*cheight*nbcols+xstart;
+ dest=ydest*cheight*nbcols+xstart;
+ outw(VGAREG_GRDC_ADDRESS, 0x0105);
+ for(i=0;i<cheight;i++)
+ {
+ memcpyb(0xa000,dest+i*nbcols,0xa000,src+i*nbcols,cols);
+ }
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_fill_pl4(xstart,ystart,cols,nbcols,cheight,attr)
+Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr;
+{
+ Bit16u dest;
+ Bit8u i;
+
+ dest=ystart*cheight*nbcols+xstart;
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ for(i=0;i<cheight;i++)
+ {
+ memsetb(0xa000,dest+i*nbcols,attr,cols);
+ }
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_copy_cga(xstart,ysrc,ydest,cols,nbcols,cheight)
+Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight;
+{
+ Bit16u src,dest;
+ Bit8u i;
+
+ src=((ysrc*cheight*nbcols)>>1)+xstart;
+ dest=((ydest*cheight*nbcols)>>1)+xstart;
+ for(i=0;i<cheight;i++)
+ {
+ if (i & 1)
+ memcpyb(0xb800,0x2000+dest+(i>>1)*nbcols,0xb800,0x2000+src+(i>>1)*nbcols,cols);
+ else
+ memcpyb(0xb800,dest+(i>>1)*nbcols,0xb800,src+(i>>1)*nbcols,cols);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_fill_cga(xstart,ystart,cols,nbcols,cheight,attr)
+Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr;
+{
+ Bit16u dest;
+ Bit8u i;
+
+ dest=((ystart*cheight*nbcols)>>1)+xstart;
+ for(i=0;i<cheight;i++)
+ {
+ if (i & 1)
+ memsetb(0xb800,0x2000+dest+(i>>1)*nbcols,attr,cols);
+ else
+ memsetb(0xb800,dest+(i>>1)*nbcols,attr,cols);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_scroll (nblines,attr,rul,cul,rlr,clr,page,dir)
+Bit8u nblines;Bit8u attr;Bit8u rul;Bit8u cul;Bit8u rlr;Bit8u clr;Bit8u page;Bit8u dir;
+{
+ // page == 0xFF if current
+
+ Bit8u mode,line,cheight,bpp,cols;
+ Bit16u nbcols,nbrows,i;
+ Bit16u address;
+
+ if(rul>rlr)return;
+ if(cul>clr)return;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ // Get the current page
+ if(page==0xFF)
+ page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ if(rlr>=nbrows)rlr=nbrows-1;
+ if(clr>=nbcols)clr=nbcols-1;
+ if(nblines>nbrows)nblines=0;
+ cols=clr-cul+1;
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page);
+#ifdef DEBUG
+ printf("Scroll, address %04x (%04x %04x %02x)\n",address,nbrows,nbcols,page);
+#endif
+
+ if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+ {
+ memsetw(vga_modes[line].sstart,address,(Bit16u)attr*0x100+' ',nbrows*nbcols);
+ }
+ else
+ {// if Scroll up
+ if(dir==SCROLL_UP)
+ {for(i=rul;i<=rlr;i++)
+ {
+ if((i+nblines>rlr)||(nblines==0))
+ memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols);
+ else
+ memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i+nblines)*nbcols+cul)*2,cols);
+ }
+ }
+ else
+ {for(i=rlr;i>=rul;i--)
+ {
+ if((i<rul+nblines)||(nblines==0))
+ memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols);
+ else
+ memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i-nblines)*nbcols+cul)*2,cols);
+ if (i>rlr) break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight);
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+ }
+ else
+ {// if Scroll up
+ if(dir==SCROLL_UP)
+ {for(i=rul;i<=rlr;i++)
+ {
+ if((i+nblines>rlr)||(nblines==0))
+ vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight);
+ }
+ }
+ else
+ {for(i=rlr;i>=rul;i--)
+ {
+ if((i<rul+nblines)||(nblines==0))
+ vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_pl4(cul,i,i-nblines,cols,nbcols,cheight);
+ if (i>rlr) break;
+ }
+ }
+ }
+ break;
+ case CGA:
+ bpp=vga_modes[line].pixbits;
+ if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+ {
+ memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight*bpp);
+ }
+ else
+ {
+ if(bpp==2)
+ {
+ cul<<=1;
+ cols<<=1;
+ nbcols<<=1;
+ }
+ // if Scroll up
+ if(dir==SCROLL_UP)
+ {for(i=rul;i<=rlr;i++)
+ {
+ if((i+nblines>rlr)||(nblines==0))
+ vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_cga(cul,i+nblines,i,cols,nbcols,cheight);
+ }
+ }
+ else
+ {for(i=rlr;i>=rul;i--)
+ {
+ if((i<rul+nblines)||(nblines==0))
+ vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_cga(cul,i,i-nblines,cols,nbcols,cheight);
+ if (i>rlr) break;
+ }
+ }
+ }
+ break;
+#ifdef DEBUG
+ default:
+ printf("Scroll in graphics mode ");
+ unimplemented();
+#endif
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_char_attr (page,car)
+Bit8u page;Bit16u *car;
+{Bit16u ss=get_SS();
+ Bit8u xcurs,ycurs,mode,line;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ write_word(ss,car,read_word(vga_modes[line].sstart,address));
+ }
+ else
+ {
+ // FIXME gfx mode
+#ifdef DEBUG
+ unimplemented();
+#endif
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u cheight;
+{
+ Bit8u i,j,mask;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ switch(cheight)
+ {case 14:
+ fdata = &vgafont14;
+ break;
+ case 16:
+ fdata = &vgafont16;
+ break;
+ default:
+ fdata = &vgafont8;
+ }
+ addr=xcurs+ycurs*cheight*nbcols;
+ src = car * cheight;
+ outw(VGAREG_SEQU_ADDRESS, 0x0f02);
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ if(attr&0x80)
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x1803);
+ }
+ else
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x0003);
+ }
+ for(i=0;i<cheight;i++)
+ {
+ dest=addr+i*nbcols;
+ for(j=0;j<8;j++)
+ {
+ mask=0x80>>j;
+ outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
+ read_byte(0xa000,dest);
+ if(fdata[src+i]&mask)
+ {
+ write_byte(0xa000,dest,attr&0x0f);
+ }
+ else
+ {
+ write_byte(0xa000,dest,0x00);
+ }
+ }
+ }
+ASM_START
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0xff08
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0003
+ out dx, ax
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u bpp;
+{
+ Bit8u i,j,mask,data;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ fdata = &vgafont8;
+ addr=(xcurs*bpp)+ycurs*320;
+ src = car * 8;
+ for(i=0;i<8;i++)
+ {
+ dest=addr+(i>>1)*80;
+ if (i & 1) dest += 0x2000;
+ mask = 0x80;
+ if (bpp == 1)
+ {
+ if (attr & 0x80)
+ {
+ data = read_byte(0xb800,dest);
+ }
+ else
+ {
+ data = 0x00;
+ }
+ for(j=0;j<8;j++)
+ {
+ if (fdata[src+i] & mask)
+ {
+ if (attr & 0x80)
+ {
+ data ^= (attr & 0x01) << (7-j);
+ }
+ else
+ {
+ data |= (attr & 0x01) << (7-j);
+ }
+ }
+ mask >>= 1;
+ }
+ write_byte(0xb800,dest,data);
+ }
+ else
+ {
+ while (mask > 0)
+ {
+ if (attr & 0x80)
+ {
+ data = read_byte(0xb800,dest);
+ }
+ else
+ {
+ data = 0x00;
+ }
+ for(j=0;j<4;j++)
+ {
+ if (fdata[src+i] & mask)
+ {
+ if (attr & 0x80)
+ {
+ data ^= (attr & 0x03) << ((3-j)*2);
+ }
+ else
+ {
+ data |= (attr & 0x03) << ((3-j)*2);
+ }
+ }
+ mask >>= 1;
+ }
+ write_byte(0xb800,dest,data);
+ dest += 1;
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;
+{
+ Bit8u i,j,mask,data;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ fdata = &vgafont8;
+ addr=xcurs*8+ycurs*nbcols*64;
+ src = car * 8;
+ for(i=0;i<8;i++)
+ {
+ dest=addr+i*nbcols*8;
+ mask = 0x80;
+ for(j=0;j<8;j++)
+ {
+ data = 0x00;
+ if (fdata[src+i] & mask)
+ {
+ data = attr;
+ }
+ write_byte(0xa000,dest+j,data);
+ mask >>= 1;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_char_attr (car,page,attr,count)
+Bit8u car;Bit8u page;Bit8u attr;Bit16u count;
+{
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ dummy=((Bit16u)attr<<8)+car;
+ memsetw(vga_modes[line].sstart,address,dummy,count);
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ bpp=vga_modes[line].pixbits;
+ while((count-->0) && (xcurs<nbcols))
+ {
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+ break;
+ case CGA:
+ write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+ break;
+ case LINEAR8:
+ write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+ xcurs++;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_char_only (car,page,attr,count)
+Bit8u car;Bit8u page;Bit8u attr;Bit16u count;
+{
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ while(count-->0)
+ {write_byte(vga_modes[line].sstart,address,car);
+ address+=2;
+ }
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ bpp=vga_modes[line].pixbits;
+ while((count-->0) && (xcurs<nbcols))
+ {
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+ break;
+ case CGA:
+ write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+ break;
+ case LINEAR8:
+ write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+ xcurs++;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_0B:
+ cmp bh, #0x00
+ je biosfn_set_border_color
+ cmp bh, #0x01
+ je biosfn_set_palette
+#ifdef DEBUG
+ call _unknown
+#endif
+ ret
+biosfn_set_border_color:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x00
+ out dx, al
+ mov al, bl
+ and al, #0x0f
+ test al, #0x08
+ jz set_low_border
+ add al, #0x08
+set_low_border:
+ out dx, al
+ mov cl, #0x01
+ and bl, #0x10
+set_intensity_loop:
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, cl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and al, #0xef
+ or al, bl
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ inc cl
+ cmp cl, #0x04
+ jne set_intensity_loop
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+biosfn_set_palette:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov cl, #0x01
+ and bl, #0x01
+set_cga_palette_loop:
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, cl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and al, #0xfe
+ or al, bl
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ inc cl
+ cmp cl, #0x04
+ jne set_cga_palette_loop
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_pixel (BH,AL,CX,DX) Bit8u BH;Bit8u AL;Bit16u CX;Bit16u DX;
+{
+ Bit8u mode,line,mask,attr,data;
+ Bit16u addr;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+ if(vga_modes[line].class==TEXT)return;
+
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ mask = 0x80 >> (CX & 0x07);
+ outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ data = read_byte(0xa000,addr);
+ if (AL & 0x80)
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x1803);
+ }
+ write_byte(0xa000,addr,AL);
+ASM_START
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0xff08
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0003
+ out dx, ax
+ASM_END
+ break;
+ case CGA:
+ if(vga_modes[line].pixbits==2)
+ {
+ addr=(CX>>2)+(DX>>1)*80;
+ }
+ else
+ {
+ addr=(CX>>3)+(DX>>1)*80;
+ }
+ if (DX & 1) addr += 0x2000;
+ data = read_byte(0xb800,addr);
+ if(vga_modes[line].pixbits==2)
+ {
+ attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
+ mask = 0x03 << ((3 - (CX & 0x03)) * 2);
+ }
+ else
+ {
+ attr = (AL & 0x01) << (7 - (CX & 0x07));
+ mask = 0x01 << (7 - (CX & 0x07));
+ }
+ if (AL & 0x80)
+ {
+ data ^= attr;
+ }
+ else
+ {
+ data &= ~mask;
+ data |= attr;
+ }
+ write_byte(0xb800,addr,data);
+ break;
+ case LINEAR8:
+ addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ write_byte(0xa000,addr,AL);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_pixel (BH,CX,DX,AX) Bit8u BH;Bit16u CX;Bit16u DX;Bit16u *AX;
+{
+ Bit8u mode,line,mask,attr,data,i;
+ Bit16u addr;
+ Bit16u ss=get_SS();
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+ if(vga_modes[line].class==TEXT)return;
+
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ mask = 0x80 >> (CX & 0x07);
+ attr = 0x00;
+ for(i=0;i<4;i++)
+ {
+ outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04);
+ data = read_byte(0xa000,addr) & mask;
+ if (data > 0) attr |= (0x01 << i);
+ }
+ break;
+ case CGA:
+ addr=(CX>>2)+(DX>>1)*80;
+ if (DX & 1) addr += 0x2000;
+ data = read_byte(0xb800,addr);
+ if(vga_modes[line].pixbits==2)
+ {
+ attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
+ }
+ else
+ {
+ attr = (data >> (7 - (CX & 0x07))) & 0x01;
+ }
+ break;
+ case LINEAR8:
+ addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ attr=read_byte(0xa000,addr);
+ break;
+ default:
+#ifdef DEBUG
+ unimplemented();
+#endif
+ attr = 0;
+ }
+ write_word(ss,AX,(read_word(ss,AX) & 0xff00) | attr);
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_teletype (car, page, attr, flag)
+Bit8u car;Bit8u page;Bit8u attr;Bit8u flag;
+{// flag = WITH_ATTR / NO_ATTR
+
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // special case if page is 0xff, use current page
+ if(page==0xff)
+ page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ switch(car)
+ {
+ case 7:
+ //FIXME should beep
+ break;
+
+ case 8:
+ if(xcurs>0)xcurs--;
+ break;
+
+ case '\r':
+ xcurs=0;
+ break;
+
+ case '\n':
+ ycurs++;
+ break;
+
+ case '\t':
+ do
+ {
+ biosfn_write_teletype(' ',page,attr,flag);
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+ }while(xcurs%8==0);
+ break;
+
+ default:
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ // Write the char
+ write_byte(vga_modes[line].sstart,address,car);
+
+ if(flag==WITH_ATTR)
+ write_byte(vga_modes[line].sstart,address+1,attr);
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ bpp=vga_modes[line].pixbits;
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+ break;
+ case CGA:
+ write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+ break;
+ case LINEAR8:
+ write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+ }
+ xcurs++;
+ }
+
+ // Do we need to wrap ?
+ if(xcurs==nbcols)
+ {xcurs=0;
+ ycurs++;
+ }
+
+ // Do we need to scroll ?
+ if(ycurs==nbrows)
+ {
+ if(vga_modes[line].class==TEXT)
+ {
+ biosfn_scroll(0x01,0x07,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
+ }
+ else
+ {
+ biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
+ }
+ ycurs-=1;
+ }
+
+ // Set the cursor for the page
+ cursor=ycurs; cursor<<=8; cursor+=xcurs;
+ biosfn_set_cursor_pos(page,cursor);
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_video_mode:
+ push ds
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ push bx
+ mov bx, # BIOSMEM_CURRENT_PAGE
+ mov al, [bx]
+ pop bx
+ mov bh, al
+ push bx
+ mov bx, # BIOSMEM_VIDEO_CTL
+ mov ah, [bx]
+ and ah, #0x80
+ mov bx, # BIOSMEM_CURRENT_MODE
+ mov al, [bx]
+ or al, ah
+ mov bx, # BIOSMEM_NB_COLS
+ mov ah, [bx]
+ pop bx
+ pop ds
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_10:
+ cmp al, #0x00
+ jne int10_test_1001
+ jmp biosfn_set_single_palette_reg
+int10_test_1001:
+ cmp al, #0x01
+ jne int10_test_1002
+ jmp biosfn_set_overscan_border_color
+int10_test_1002:
+ cmp al, #0x02
+ jne int10_test_1003
+ jmp biosfn_set_all_palette_reg
+int10_test_1003:
+ cmp al, #0x03
+ jne int10_test_1007
+ jmp biosfn_toggle_intensity
+int10_test_1007:
+ cmp al, #0x07
+ jne int10_test_1008
+ jmp biosfn_get_single_palette_reg
+int10_test_1008:
+ cmp al, #0x08
+ jne int10_test_1009
+ jmp biosfn_read_overscan_border_color
+int10_test_1009:
+ cmp al, #0x09
+ jne int10_test_1010
+ jmp biosfn_get_all_palette_reg
+int10_test_1010:
+ cmp al, #0x10
+ jne int10_test_1012
+ jmp biosfn_set_single_dac_reg
+int10_test_1012:
+ cmp al, #0x12
+ jne int10_test_1013
+ jmp biosfn_set_all_dac_reg
+int10_test_1013:
+ cmp al, #0x13
+ jne int10_test_1015
+ jmp biosfn_select_video_dac_color_page
+int10_test_1015:
+ cmp al, #0x15
+ jne int10_test_1017
+ jmp biosfn_read_single_dac_reg
+int10_test_1017:
+ cmp al, #0x17
+ jne int10_test_1018
+ jmp biosfn_read_all_dac_reg
+int10_test_1018:
+ cmp al, #0x18
+ jne int10_test_1019
+ jmp biosfn_set_pel_mask
+int10_test_1019:
+ cmp al, #0x19
+ jne int10_test_101A
+ jmp biosfn_read_pel_mask
+int10_test_101A:
+ cmp al, #0x1a
+ jne int10_group_10_unknown
+ jmp biosfn_read_video_dac_state
+int10_group_10_unknown:
+#ifdef DEBUG
+ call _unknown
+#endif
+ ret
+
+biosfn_set_single_palette_reg:
+ cmp bl, #0x14
+ ja no_actl_reg1
+ push ax
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, bl
+ out dx, al
+ mov al, bh
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop ax
+no_actl_reg1:
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_overscan_border_color:
+ push bx
+ mov bl, #0x11
+ call biosfn_set_single_palette_reg
+ pop bx
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_all_palette_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov bx, dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov cl, #0x00
+ mov dx, # VGAREG_ACTL_ADDRESS
+set_palette_loop:
+ mov al, cl
+ out dx, al
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ inc cl
+ cmp cl, #0x10
+ jne set_palette_loop
+ mov al, #0x11
+ out dx, al
+ seg es
+ mov al, [bx]
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_toggle_intensity:
+ push ax
+ push bx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and al, #0xf7
+ and bl, #0x01
+ shl bl, 3
+ or al, bl
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_single_palette_reg:
+ cmp bl, #0x14
+ ja no_actl_reg2
+ push ax
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, bl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ mov bh, al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop ax
+no_actl_reg2:
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_overscan_border_color:
+ push ax
+ push bx
+ mov bl, #0x11
+ call biosfn_get_single_palette_reg
+ mov al, bh
+ pop bx
+ mov bh, al
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_all_palette_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov bx, dx
+ mov cl, #0x00
+get_palette_loop:
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, cl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ inc cl
+ cmp cl, #0x10
+ jne get_palette_loop
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x11
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ seg es
+ mov [bx], al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_single_dac_reg:
+ push ax
+ push dx
+ mov dx, # VGAREG_DAC_WRITE_ADDRESS
+ mov al, bl
+ out dx, al
+ mov dx, # VGAREG_DAC_DATA
+ pop ax
+ push ax
+ mov al, ah
+ out dx, al
+ mov al, ch
+ out dx, al
+ mov al, cl
+ out dx, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_all_dac_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_DAC_WRITE_ADDRESS
+ mov al, bl
+ out dx, al
+ pop dx
+ push dx
+ mov bx, dx
+ mov dx, # VGAREG_DAC_DATA
+set_dac_loop:
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ dec cx
+ jnz set_dac_loop
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_select_video_dac_color_page:
+ push ax
+ push bx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and bl, #0x01
+ jnz set_dac_page
+ and al, #0x7f
+ shl bh, 7
+ or al, bh
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ jmp set_actl_normal
+set_dac_page:
+ push ax
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x14
+ out dx, al
+ pop ax
+ and al, #0x80
+ jnz set_dac_16_page
+ shl bh, 2
+set_dac_16_page:
+ and bh, #0x0f
+ mov al, bh
+ out dx, al
+set_actl_normal:
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_single_dac_reg:
+ push ax
+ push dx
+ mov dx, # VGAREG_DAC_READ_ADDRESS
+ mov al, bl
+ out dx, al
+ pop ax
+ mov ah, al
+ mov dx, # VGAREG_DAC_DATA
+ in al, dx
+ xchg al, ah
+ push ax
+ in al, dx
+ mov ch, al
+ in al, dx
+ mov cl, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_all_dac_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_DAC_READ_ADDRESS
+ mov al, bl
+ out dx, al
+ pop dx
+ push dx
+ mov bx, dx
+ mov dx, # VGAREG_DAC_DATA
+read_dac_loop:
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ dec cx
+ jnz read_dac_loop
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_pel_mask:
+ push ax
+ push dx
+ mov dx, # VGAREG_PEL_MASK
+ mov al, bl
+ out dx, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_pel_mask:
+ push ax
+ push dx
+ mov dx, # VGAREG_PEL_MASK
+ in al, dx
+ mov bl, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_video_dac_state:
+ push ax
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ mov bl, al
+ shr bl, 7
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x14
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ mov bh, al
+ and bh, #0x0f
+ test bl, #0x01
+ jnz get_dac_16_page
+ shr bh, 2
+get_dac_16_page:
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_perform_gray_scale_summing (start,count)
+Bit16u start;Bit16u count;
+{Bit8u r,g,b;
+ Bit16u i;
+ Bit16u index;
+
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS,0x00);
+
+ for( index = 0; index < count; index++ )
+ {
+ // set read address and switch to read mode
+ outb(VGAREG_DAC_READ_ADDRESS,start);
+ // get 6-bit wide RGB data values
+ r=inb( VGAREG_DAC_DATA );
+ g=inb( VGAREG_DAC_DATA );
+ b=inb( VGAREG_DAC_DATA );
+
+ // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue )
+ i = ( ( 77*r + 151*g + 28*b ) + 0x80 ) >> 8;
+
+ if(i>0x3f)i=0x3f;
+
+ // set write address and switch to write mode
+ outb(VGAREG_DAC_WRITE_ADDRESS,start);
+ // write new intensity value
+ outb( VGAREG_DAC_DATA, i&0xff );
+ outb( VGAREG_DAC_DATA, i&0xff );
+ outb( VGAREG_DAC_DATA, i&0xff );
+ start++;
+ }
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS,0x20);
+}
+
+// --------------------------------------------------------------------------------------------
+static void get_font_access()
+{
+ASM_START
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0100
+ out dx, ax
+ mov ax, #0x0402
+ out dx, ax
+ mov ax, #0x0704
+ out dx, ax
+ mov ax, #0x0300
+ out dx, ax
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0x0204
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0406
+ out dx, ax
+ASM_END
+}
+
+static void release_font_access()
+{
+ASM_START
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0100
+ out dx, ax
+ mov ax, #0x0302
+ out dx, ax
+ mov ax, #0x0304
+ out dx, ax
+ mov ax, #0x0300
+ out dx, ax
+ mov dx, # VGAREG_READ_MISC_OUTPUT
+ in al, dx
+ and al, #0x01
+ shl al, 2
+ or al, #0x0a
+ mov ah, al
+ mov al, #0x06
+ mov dx, # VGAREG_GRDC_ADDRESS
+ out dx, ax
+ mov ax, #0x0004
+ out dx, ax
+ mov ax, #0x1005
+ out dx, ax
+ASM_END
+}
+
+ASM_START
+idiv_u:
+ xor dx,dx
+ div bx
+ ret
+ASM_END
+
+static void set_scan_lines(lines) Bit8u lines;
+{
+ Bit16u crtc_addr,cols,page,vde;
+ Bit8u crtc_r9,ovl,rows;
+
+ crtc_addr = read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr, 0x09);
+ crtc_r9 = inb(crtc_addr+1);
+ crtc_r9 = (crtc_r9 & 0xe0) | (lines - 1);
+ outb(crtc_addr+1, crtc_r9);
+ if(lines==8)
+ {
+ biosfn_set_cursor_shape(0x06,0x07);
+ }
+ else
+ {
+ biosfn_set_cursor_shape(lines-4,lines-3);
+ }
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, lines);
+ outb(crtc_addr, 0x12);
+ vde = inb(crtc_addr+1);
+ outb(crtc_addr, 0x07);
+ ovl = inb(crtc_addr+1);
+ vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1);
+ rows = vde / lines;
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, rows-1);
+ cols = read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, rows * cols * 2);
+}
+
+static void biosfn_load_text_user_pat (AL,ES,BP,CX,DX,BL,BH) Bit8u AL;Bit16u ES;Bit16u BP;Bit16u CX;Bit16u DX;Bit8u BL;Bit8u BH;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<CX;i++)
+ {
+ src = BP + i * BH;
+ dest = blockaddr + (DX + i) * 32;
+ memcpyb(0xA000, dest, ES, src, BH);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(BH);
+ }
+}
+
+static void biosfn_load_text_8_14_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+ {
+ src = i * 14;
+ dest = blockaddr + i * 32;
+ memcpyb(0xA000, dest, 0xC000, vgafont14+src, 14);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(14);
+ }
+}
+
+static void biosfn_load_text_8_8_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+ {
+ src = i * 8;
+ dest = blockaddr + i * 32;
+ memcpyb(0xA000, dest, 0xC000, vgafont8+src, 8);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(8);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_text_block_specifier:
+ push ax
+ push dx
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ah, bl
+ mov al, #0x03
+ out dx, ax
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_load_text_8_16_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+ {
+ src = i * 16;
+ dest = blockaddr + i * 32;
+ memcpyb(0xA000, dest, 0xC000, vgafont16+src, 16);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(16);
+ }
+}
+
+static void biosfn_load_gfx_8_8_chars (ES,BP) Bit16u ES;Bit16u BP;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_user_chars (ES,BP,CX,BL,DL) Bit16u ES;Bit16u BP;Bit16u CX;Bit8u BL;Bit8u DL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_14_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_8_dd_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_16_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+// --------------------------------------------------------------------------------------------
+static void biosfn_get_font_info (BH,ES,BP,CX,DX)
+Bit8u BH;Bit16u *ES;Bit16u *BP;Bit16u *CX;Bit16u *DX;
+{Bit16u ss=get_SS();
+
+ switch(BH)
+ {case 0x00:
+ write_word(ss,ES,read_word(0x00,0x1f*4));
+ write_word(ss,BP,read_word(0x00,(0x1f*4)+2));
+ break;
+ case 0x01:
+ write_word(ss,ES,read_word(0x00,0x43*4));
+ write_word(ss,BP,read_word(0x00,(0x43*4)+2));
+ break;
+ case 0x02:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont14);
+ break;
+ case 0x03:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont8);
+ break;
+ case 0x04:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont8+128*8);
+ break;
+ case 0x05:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont14alt);
+ break;
+ case 0x06:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont16);
+ break;
+ case 0x07:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont16alt);
+ break;
+ default:
+ #ifdef DEBUG
+ printf("Get font info BH(%02x) was discarded\n",BH);
+ #endif
+ return;
+ }
+ // Set byte/char of on screen font
+ write_word(ss,CX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT));
+
+ // Set Highest char row
+ write_word(ss,DX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS));
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_ega_info:
+ push ds
+ push ax
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ xor ch, ch
+ mov bx, # BIOSMEM_SWITCHES
+ mov cl, [bx]
+ and cl, #0x0f
+ mov bx, # BIOSMEM_CRTC_ADDRESS
+ mov ax, [bx]
+ mov bx, #0x0003
+ cmp ax, # VGAREG_MDA_CRTC_ADDRESS
+ jne mode_ega_color
+ mov bh, #0x01
+mode_ega_color:
+ pop ax
+ pop ds
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_alternate_prtsc()
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_select_vert_res:
+
+; res : 00 200 lines, 01 350 lines, 02 400 lines
+
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ mov bx, # BIOSMEM_SWITCHES
+ mov ah, [bx]
+ cmp dl, #0x01
+ je vert_res_350
+ jb vert_res_200
+ cmp dl, #0x02
+ je vert_res_400
+#ifdef DEBUG
+ mov al, dl
+ xor ah, ah
+ push ax
+ mov bx, #msg_vert_res
+ push bx
+ call _printf
+ add sp, #4
+#endif
+ jmp set_retcode
+vert_res_400:
+
+ ; reset modeset ctl bit 7 and set bit 4
+ ; set switches bit 3-0 to 0x09
+
+ and al, #0x7f
+ or al, #0x10
+ and ah, #0xf0
+ or ah, #0x09
+ jnz set_vert_res
+vert_res_350:
+
+ ; reset modeset ctl bit 7 and bit 4
+ ; set switches bit 3-0 to 0x09
+
+ and al, #0x6f
+ and ah, #0xf0
+ or ah, #0x09
+ jnz set_vert_res
+vert_res_200:
+
+ ; set modeset ctl bit 7 and reset bit 4
+ ; set switches bit 3-0 to 0x08
+
+ and al, #0xef
+ or al, #0x80
+ and ah, #0xf0
+ or ah, #0x08
+set_vert_res:
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov [bx], al
+ mov bx, # BIOSMEM_SWITCHES
+ mov [bx], ah
+set_retcode:
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+#ifdef DEBUG
+msg_vert_res:
+.ascii "Select vert res (%02x) was discarded"
+.byte 0x0d,0x0a,0x00
+#endif
+
+
+biosfn_enable_default_palette_loading:
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ and dl, #0x01
+ shl dl, 3
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ and al, #0xf7
+ or al, dl
+ mov [bx], al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+
+biosfn_enable_video_addressing:
+ push bx
+ push dx
+ mov bl, al
+ and bl, #0x01
+ xor bl, #0x01
+ shl bl, 1
+ mov dx, # VGAREG_READ_MISC_OUTPUT
+ in al, dx
+ and al, #0xfd
+ or al, bl
+ mov dx, # VGAREG_WRITE_MISC_OUTPUT
+ out dx, al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ ret
+
+
+biosfn_enable_grayscale_summing:
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ and dl, #0x01
+ xor dl, #0x01
+ shl dl, 1
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ and al, #0xfd
+ or al, dl
+ mov [bx], al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+
+biosfn_enable_cursor_emulation:
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ and dl, #0x01
+ xor dl, #0x01
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ and al, #0xfe
+ or al, dl
+ mov [bx], al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_switch_video_interface (AL,ES,DX) Bit8u AL;Bit16u ES;Bit16u DX;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_enable_video_refresh_control (AL) Bit8u AL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_string (flag,page,attr,count,row,col,seg,offset)
+Bit8u flag;Bit8u page;Bit8u attr;Bit16u count;Bit8u row;Bit8u col;Bit16u seg;Bit16u offset;
+{
+ Bit16u newcurs,oldcurs,dummy;
+ Bit8u car,carattr;
+
+ // Read curs info for the page
+ biosfn_get_cursor_pos(page,&dummy,&oldcurs);
+
+ // if row=0xff special case : use current cursor position
+ if(row==0xff)
+ {col=oldcurs&0x00ff;
+ row=(oldcurs&0xff00)>>8;
+ }
+
+ newcurs=row; newcurs<<=8; newcurs+=col;
+ biosfn_set_cursor_pos(page,newcurs);
+
+ while(count--!=0)
+ {
+ car=read_byte(seg,offset++);
+ if((flag&0x02)!=0)
+ attr=read_byte(seg,offset++);
+
+ biosfn_write_teletype(car,page,attr,WITH_ATTR);
+ }
+
+ // Set back curs pos
+ if((flag&0x01)==0)
+ biosfn_set_cursor_pos(page,oldcurs);
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_1A:
+ cmp al, #0x00
+ je biosfn_read_display_code
+ cmp al, #0x01
+ je biosfn_set_display_code
+#ifdef DEBUG
+ call _unknown
+#endif
+ ret
+biosfn_read_display_code:
+ push ds
+ push ax
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_DCC_INDEX
+ mov al, [bx]
+ mov bl, al
+ xor bh, bh
+ pop ax
+ mov al, ah
+ pop ds
+ ret
+biosfn_set_display_code:
+ push ds
+ push ax
+ push bx
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov ax, bx
+ mov bx, # BIOSMEM_DCC_INDEX
+ mov [bx], al
+#ifdef DEBUG
+ mov al, ah
+ xor ah, ah
+ push ax
+ mov bx, #msg_alt_dcc
+ push bx
+ call _printf
+ add sp, #4
+#endif
+ pop bx
+ pop ax
+ mov al, ah
+ pop ds
+ ret
+
+#ifdef DEBUG
+msg_alt_dcc:
+.ascii "Alternate Display code (%02x) was discarded"
+.byte 0x0d,0x0a,0x00
+#endif
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_state_info (BX,ES,DI)
+Bit16u BX;Bit16u ES;Bit16u DI;
+{
+ // Address of static functionality table
+ write_word(ES,DI+0x00,&static_functionality);
+ write_word(ES,DI+0x02,0xC000);
+
+ // Hard coded copy from BIOS area. Should it be cleaner ?
+ memcpyb(ES,DI+0x04,BIOSMEM_SEG,0x49,30);
+ memcpyb(ES,DI+0x22,BIOSMEM_SEG,0x84,3);
+
+ write_byte(ES,DI+0x25,read_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX));
+ write_byte(ES,DI+0x26,0);
+ write_byte(ES,DI+0x27,16);
+ write_byte(ES,DI+0x28,0);
+ write_byte(ES,DI+0x29,8);
+ write_byte(ES,DI+0x2a,2);
+ write_byte(ES,DI+0x2b,0);
+ write_byte(ES,DI+0x2c,0);
+ write_byte(ES,DI+0x31,3);
+ write_byte(ES,DI+0x32,0);
+
+ memsetb(ES,DI+0x33,0,13);
+}
+
+// --------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------
+static Bit16u biosfn_read_video_state_size2 (CX)
+ Bit16u CX;
+{
+ Bit16u size;
+ size = 0;
+ if (CX & 1) {
+ size += 0x46;
+ }
+ if (CX & 2) {
+ size += (5 + 8 + 5) * 2 + 6;
+ }
+ if (CX & 4) {
+ size += 3 + 256 * 3 + 1;
+}
+ return size;
+}
+
+static void biosfn_read_video_state_size (CX, BX)
+ Bit16u CX; Bit16u *BX;
+{
+ Bit16u ss=get_SS();
+ write_word(ss, BX, biosfn_read_video_state_size2(CX));
+}
+
+static Bit16u biosfn_save_video_state (CX,ES,BX)
+ Bit16u CX;Bit16u ES;Bit16u BX;
+{
+ Bit16u i, v, crtc_addr, ar_index;
+
+ crtc_addr = read_word(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS);
+ if (CX & 1) {
+ write_byte(ES, BX, inb(VGAREG_SEQU_ADDRESS)); BX++;
+ write_byte(ES, BX, inb(crtc_addr)); BX++;
+ write_byte(ES, BX, inb(VGAREG_GRDC_ADDRESS)); BX++;
+ inb(VGAREG_ACTL_RESET);
+ ar_index = inb(VGAREG_ACTL_ADDRESS);
+ write_byte(ES, BX, ar_index); BX++;
+ write_byte(ES, BX, inb(VGAREG_READ_FEATURE_CTL)); BX++;
+
+ for(i=1;i<=4;i++){
+ outb(VGAREG_SEQU_ADDRESS, i);
+ write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
+ }
+ outb(VGAREG_SEQU_ADDRESS, 0);
+ write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
+
+ for(i=0;i<=0x18;i++) {
+ outb(crtc_addr,i);
+ write_byte(ES, BX, inb(crtc_addr+1)); BX++;
+ }
+
+ for(i=0;i<=0x13;i++) {
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
+ write_byte(ES, BX, inb(VGAREG_ACTL_READ_DATA)); BX++;
+ }
+ inb(VGAREG_ACTL_RESET);
+
+ for(i=0;i<=8;i++) {
+ outb(VGAREG_GRDC_ADDRESS,i);
+ write_byte(ES, BX, inb(VGAREG_GRDC_DATA)); BX++;
+ }
+
+ write_word(ES, BX, crtc_addr); BX+= 2;
+
+ /* XXX: read plane latches */
+ write_byte(ES, BX, 0); BX++;
+ write_byte(ES, BX, 0); BX++;
+ write_byte(ES, BX, 0); BX++;
+ write_byte(ES, BX, 0); BX++;
+ }
+ if (CX & 2) {
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)); BX++;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)); BX += 2;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)); BX += 2;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)); BX += 2;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); BX++;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); BX += 2;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)); BX++;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES)); BX++;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)); BX++;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); BX += 2;
+ for(i=0;i<8;i++) {
+ write_word(ES, BX, read_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i));
+ BX += 2;
+ }
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START)); BX += 2;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)); BX++;
+ /* current font */
+ write_word(ES, BX, read_word(0, 0x1f * 4)); BX += 2;
+ write_word(ES, BX, read_word(0, 0x1f * 4 + 2)); BX += 2;
+ write_word(ES, BX, read_word(0, 0x43 * 4)); BX += 2;
+ write_word(ES, BX, read_word(0, 0x43 * 4 + 2)); BX += 2;
+ }
+ if (CX & 4) {
+ /* XXX: check this */
+ write_byte(ES, BX, inb(VGAREG_DAC_STATE)); BX++; /* read/write mode dac */
+ write_byte(ES, BX, inb(VGAREG_DAC_WRITE_ADDRESS)); BX++; /* pix address */
+ write_byte(ES, BX, inb(VGAREG_PEL_MASK)); BX++;
+ // Set the whole dac always, from 0
+ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+ for(i=0;i<256*3;i++) {
+ write_byte(ES, BX, inb(VGAREG_DAC_DATA)); BX++;
+ }
+ write_byte(ES, BX, 0); BX++; /* color select register */
+ }
+ return BX;
+}
+
+static Bit16u biosfn_restore_video_state (CX,ES,BX)
+ Bit16u CX;Bit16u ES;Bit16u BX;
+{
+ Bit16u i, crtc_addr, v, addr1, ar_index;
+
+ if (CX & 1) {
+ // Reset Attribute Ctl flip-flop
+ inb(VGAREG_ACTL_RESET);
+
+ crtc_addr = read_word(ES, BX + 0x40);
+ addr1 = BX;
+ BX += 5;
+
+ for(i=1;i<=4;i++){
+ outb(VGAREG_SEQU_ADDRESS, i);
+ outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
+ }
+ outb(VGAREG_SEQU_ADDRESS, 0);
+ outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
+
+ // Disable CRTC write protection
+ outw(crtc_addr,0x0011);
+ // Set CRTC regs
+ for(i=0;i<=0x18;i++) {
+ if (i != 0x11) {
+ outb(crtc_addr,i);
+ outb(crtc_addr+1, read_byte(ES, BX));
+ }
+ BX++;
+ }
+ // select crtc base address
+ v = inb(VGAREG_READ_MISC_OUTPUT) & ~0x01;
+ if (crtc_addr = 0x3d4)
+ v |= 0x01;
+ outb(VGAREG_WRITE_MISC_OUTPUT, v);
+
+ // enable write protection if needed
+ outb(crtc_addr, 0x11);
+ outb(crtc_addr+1, read_byte(ES, BX - 0x18 + 0x11));
+
+ // Set Attribute Ctl
+ ar_index = read_byte(ES, addr1 + 0x03);
+ inb(VGAREG_ACTL_RESET);
+ for(i=0;i<=0x13;i++) {
+ outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
+ outb(VGAREG_ACTL_WRITE_DATA, read_byte(ES, BX)); BX++;
+ }
+ outb(VGAREG_ACTL_ADDRESS, ar_index);
+ inb(VGAREG_ACTL_RESET);
+
+ for(i=0;i<=8;i++) {
+ outb(VGAREG_GRDC_ADDRESS,i);
+ outb(VGAREG_GRDC_DATA, read_byte(ES, BX)); BX++;
+ }
+ BX += 2; /* crtc_addr */
+ BX += 4; /* plane latches */
+
+ outb(VGAREG_SEQU_ADDRESS, read_byte(ES, addr1)); addr1++;
+ outb(crtc_addr, read_byte(ES, addr1)); addr1++;
+ outb(VGAREG_GRDC_ADDRESS, read_byte(ES, addr1)); addr1++;
+ addr1++;
+ outb(crtc_addr - 0x4 + 0xa, read_byte(ES, addr1)); addr1++;
+ }
+ if (CX & 2) {
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE, read_byte(ES, BX)); BX++;
+ write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS, read_word(ES, BX)); BX += 2;
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, read_word(ES, BX)); BX += 2;
+ write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS, read_word(ES, BX)); BX += 2;
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, read_byte(ES, BX)); BX++;
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, read_word(ES, BX)); BX += 2;
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL, read_byte(ES, BX)); BX++;
+ write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES, read_byte(ES, BX)); BX++;
+ write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL, read_byte(ES, BX)); BX++;
+ write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE, read_word(ES, BX)); BX += 2;
+ for(i=0;i<8;i++) {
+ write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i, read_word(ES, BX));
+ BX += 2;
+ }
+ write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START, read_word(ES, BX)); BX += 2;
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE, read_byte(ES, BX)); BX++;
+ /* current font */
+ write_word(0, 0x1f * 4, read_word(ES, BX)); BX += 2;
+ write_word(0, 0x1f * 4 + 2, read_word(ES, BX)); BX += 2;
+ write_word(0, 0x43 * 4, read_word(ES, BX)); BX += 2;
+ write_word(0, 0x43 * 4 + 2, read_word(ES, BX)); BX += 2;
+ }
+ if (CX & 4) {
+ BX++;
+ v = read_byte(ES, BX); BX++;
+ outb(VGAREG_PEL_MASK, read_byte(ES, BX)); BX++;
+ // Set the whole dac always, from 0
+ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+ for(i=0;i<256*3;i++) {
+ outb(VGAREG_DAC_DATA, read_byte(ES, BX)); BX++;
+ }
+ BX++;
+ outb(VGAREG_DAC_WRITE_ADDRESS, v);
+ }
+ return BX;
+}
+
+// ============================================================================================
+//
+// Video Utils
+//
+// ============================================================================================
+
+// --------------------------------------------------------------------------------------------
+static Bit8u find_vga_entry(mode)
+Bit8u mode;
+{
+ Bit8u i,line=0xFF;
+ for(i=0;i<=MODE_MAX;i++)
+ if(vga_modes[i].svgamode==mode)
+ {line=i;
+ break;
+ }
+ return line;
+}
+
+/* =========================================================== */
+/*
+ * Misc Utils
+*/
+/* =========================================================== */
+
+// --------------------------------------------------------------------------------------------
+static void memsetb(seg,offset,value,count)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u value;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+
+ mov cx, 10[bp] ; count
+ cmp cx, #0x00
+ je memsetb_end
+ mov ax, 4[bp] ; segment
+ mov es, ax
+ mov ax, 6[bp] ; offset
+ mov di, ax
+ mov al, 8[bp] ; value
+ cld
+ rep
+ stosb
+
+memsetb_end:
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memsetw(seg,offset,value,count)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u value;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+
+ mov cx, 10[bp] ; count
+ cmp cx, #0x00
+ je memsetw_end
+ mov ax, 4[bp] ; segment
+ mov es, ax
+ mov ax, 6[bp] ; offset
+ mov di, ax
+ mov ax, 8[bp] ; value
+ cld
+ rep
+ stosw
+
+memsetw_end:
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memcpyb(dseg,doffset,sseg,soffset,count)
+ Bit16u dseg;
+ Bit16u doffset;
+ Bit16u sseg;
+ Bit16u soffset;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+ push ds
+ push si
+
+ mov cx, 12[bp] ; count
+ cmp cx, #0x0000
+ je memcpyb_end
+ mov ax, 4[bp] ; dsegment
+ mov es, ax
+ mov ax, 6[bp] ; doffset
+ mov di, ax
+ mov ax, 8[bp] ; ssegment
+ mov ds, ax
+ mov ax, 10[bp] ; soffset
+ mov si, ax
+ cld
+ rep
+ movsb
+
+memcpyb_end:
+ pop si
+ pop ds
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memcpyw(dseg,doffset,sseg,soffset,count)
+ Bit16u dseg;
+ Bit16u doffset;
+ Bit16u sseg;
+ Bit16u soffset;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+ push ds
+ push si
+
+ mov cx, 12[bp] ; count
+ cmp cx, #0x0000
+ je memcpyw_end
+ mov ax, 4[bp] ; dsegment
+ mov es, ax
+ mov ax, 6[bp] ; doffset
+ mov di, ax
+ mov ax, 8[bp] ; ssegment
+ mov ds, ax
+ mov ax, 10[bp] ; soffset
+ mov si, ax
+ cld
+ rep
+ movsw
+
+memcpyw_end:
+ pop si
+ pop ds
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+/* =========================================================== */
+/*
+ * These functions where ripped from Kevin's rombios.c
+*/
+/* =========================================================== */
+
+// --------------------------------------------------------------------------------------------
+static Bit8u
+read_byte(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov al, [bx]
+ ;; al = return value (byte)
+ pop ds
+ pop bx
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static Bit16u
+read_word(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, [bx]
+ ;; ax = return value (word)
+ pop ds
+ pop bx
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void
+write_byte(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit8u data;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov al, 8[bp] ; data byte
+ mov [bx], al ; write data byte
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void
+write_word(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u data;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, 8[bp] ; data word
+ mov [bx], ax ; write data word
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ Bit8u
+inb(port)
+ Bit16u port;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push dx
+ mov dx, 4[bp]
+ in al, dx
+ pop dx
+
+ pop bp
+ASM_END
+}
+
+ Bit16u
+inw(port)
+ Bit16u port;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push dx
+ mov dx, 4[bp]
+ in ax, dx
+ pop dx
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ void
+outb(port, val)
+ Bit16u port;
+ Bit8u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push dx
+ mov dx, 4[bp]
+ mov al, 6[bp]
+ out dx, al
+ pop dx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ void
+outw(port, val)
+ Bit16u port;
+ Bit16u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push dx
+ mov dx, 4[bp]
+ mov ax, 6[bp]
+ out dx, ax
+ pop dx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+Bit16u get_SS()
+{
+ASM_START
+ mov ax, ss
+ASM_END
+}
+
+#ifdef DEBUG
+void unimplemented()
+{
+ printf("--> Unimplemented\n");
+}
+
+void unknown()
+{
+ printf("--> Unknown int10\n");
+}
+#endif
+
+// --------------------------------------------------------------------------------------------
+#if defined(USE_BX_INFO) || defined(DEBUG) || defined(CIRRUS_DEBUG)
+void printf(s)
+ Bit8u *s;
+{
+ Bit8u c, format_char;
+ Boolean in_format;
+ unsigned format_width, i;
+ Bit16u *arg_ptr;
+ Bit16u arg_seg, arg, digit, nibble, shift_count;
+
+ arg_ptr = &s;
+ arg_seg = get_SS();
+
+ in_format = 0;
+ format_width = 0;
+
+ while (c = read_byte(0xc000, s)) {
+ if ( c == '%' ) {
+ in_format = 1;
+ format_width = 0;
+ }
+ else if (in_format) {
+ if ( (c>='0') && (c<='9') ) {
+ format_width = (format_width * 10) + (c - '0');
+ }
+ else if (c == 'x') {
+ arg_ptr++; // increment to next arg
+ arg = read_word(arg_seg, arg_ptr);
+ if (format_width == 0)
+ format_width = 4;
+ i = 0;
+ digit = format_width - 1;
+ for (i=0; i<format_width; i++) {
+ nibble = (arg >> (4 * digit)) & 0x000f;
+ if (nibble <= 9)
+ outb(0x0500, nibble + '0');
+ else
+ outb(0x0500, (nibble - 10) + 'A');
+ digit--;
+ }
+ in_format = 0;
+ }
+ //else if (c == 'd') {
+ // in_format = 0;
+ // }
+ }
+ else {
+ outb(0x0500, c);
+ }
+ s ++;
+ }
+}
+#endif
+
+#ifdef VBE
+#include "vbe.c"
+#endif
+
+#ifdef CIRRUS
+#include "clext.c"
+#endif
+
+// --------------------------------------------------------------------------------------------
+
+ASM_START
+;; DATA_SEG_DEFS_HERE
+ASM_END
+
+ASM_START
+.ascii "vgabios ends here"
+.byte 0x00
+vgabios_end:
+.byte 0xCB
+;; BLOCK_STRINGS_BEGIN
+ASM_END
diff --git a/kvm/vgabios/vgabios.h b/kvm/vgabios/vgabios.h
new file mode 100644
index 000000000..3ad4bae94
--- /dev/null
+++ b/kvm/vgabios/vgabios.h
@@ -0,0 +1,47 @@
+#ifndef vgabios_h_included
+#define vgabios_h_included
+
+/* Types */
+typedef unsigned char Bit8u;
+typedef unsigned short Bit16u;
+typedef unsigned long Bit32u;
+typedef unsigned short Boolean;
+
+/* Defines */
+
+#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
+#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
+#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
+#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
+#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
+#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
+#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
+#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
+
+#define GET_AL() ( AX & 0x00ff )
+#define GET_BL() ( BX & 0x00ff )
+#define GET_CL() ( CX & 0x00ff )
+#define GET_DL() ( DX & 0x00ff )
+#define GET_AH() ( AX >> 8 )
+#define GET_BH() ( BX >> 8 )
+#define GET_CH() ( CX >> 8 )
+#define GET_DH() ( DX >> 8 )
+
+#define SET_CF() FLAGS |= 0x0001
+#define CLEAR_CF() FLAGS &= 0xfffe
+#define GET_CF() (FLAGS & 0x0001)
+
+#define SET_ZF() FLAGS |= 0x0040
+#define CLEAR_ZF() FLAGS &= 0xffbf
+#define GET_ZF() (FLAGS & 0x0040)
+
+#define SCROLL_DOWN 0
+#define SCROLL_UP 1
+#define NO_ATTR 2
+#define WITH_ATTR 3
+
+#define SCREEN_SIZE(x,y) (((x*y*2)|0x00ff)+1)
+#define SCREEN_MEM_START(x,y,p) ((((x*y*2)|0x00ff)+1)*p)
+#define SCREEN_IO_START(x,y,p) ((((x*y)|0x00ff)+1)*p)
+
+#endif
diff --git a/kvm/vgabios/vgafonts.h b/kvm/vgabios/vgafonts.h
new file mode 100644
index 000000000..0c213e66b
--- /dev/null
+++ b/kvm/vgabios/vgafonts.h
@@ -0,0 +1,784 @@
+/*
+ * These fonts come from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+ * The package is (c) by Joseph Gil
+ * The individual fonts are public domain
+ */
+static Bit8u vgafont8[256*8]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+ 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00,
+ 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38,
+ 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
+ 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00,
+ 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00,
+ 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00,
+ 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00,
+ 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18,
+ 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00,
+ 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30,
+ 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7,
+ 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70,
+ 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00,
+ 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00,
+ 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00,
+ 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f,
+ 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00,
+ 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00,
+ 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0,
+ 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00,
+ 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
+ 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00,
+ 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0,
+ 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
+ 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00,
+ 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00,
+ 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0,
+ 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00,
+ 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00,
+ 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00,
+ 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00,
+ 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70,
+ 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c,
+ 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static Bit8u vgafont14[256*14]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0x40, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static Bit8u vgafont16[256*16]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static Bit8u vgafont14alt[1]={0x00};
+static Bit8u vgafont16alt[1]={0x00};
diff --git a/kvm/vgabios/vgatables.h b/kvm/vgabios/vgatables.h
new file mode 100644
index 000000000..3ac96bbbb
--- /dev/null
+++ b/kvm/vgabios/vgatables.h
@@ -0,0 +1,622 @@
+/*
+ *
+ * BIOS Memory
+ *
+ */
+#define BIOSMEM_SEG 0x40
+
+#define BIOSMEM_INITIAL_MODE 0x10
+#define BIOSMEM_CURRENT_MODE 0x49
+#define BIOSMEM_NB_COLS 0x4A
+#define BIOSMEM_PAGE_SIZE 0x4C
+#define BIOSMEM_CURRENT_START 0x4E
+#define BIOSMEM_CURSOR_POS 0x50
+#define BIOSMEM_CURSOR_TYPE 0x60
+#define BIOSMEM_CURRENT_PAGE 0x62
+#define BIOSMEM_CRTC_ADDRESS 0x63
+#define BIOSMEM_CURRENT_MSR 0x65
+#define BIOSMEM_CURRENT_PAL 0x66
+#define BIOSMEM_NB_ROWS 0x84
+#define BIOSMEM_CHAR_HEIGHT 0x85
+#define BIOSMEM_VIDEO_CTL 0x87
+#define BIOSMEM_SWITCHES 0x88
+#define BIOSMEM_MODESET_CTL 0x89
+#define BIOSMEM_DCC_INDEX 0x8A
+#define BIOSMEM_VS_POINTER 0xA8
+#define BIOSMEM_VBE_FLAG 0xB9
+#define BIOSMEM_VBE_MODE 0xBA
+
+
+/*
+ *
+ * VGA registers
+ *
+ */
+#define VGAREG_ACTL_ADDRESS 0x3c0
+#define VGAREG_ACTL_WRITE_DATA 0x3c0
+#define VGAREG_ACTL_READ_DATA 0x3c1
+
+#define VGAREG_INPUT_STATUS 0x3c2
+#define VGAREG_WRITE_MISC_OUTPUT 0x3c2
+#define VGAREG_VIDEO_ENABLE 0x3c3
+#define VGAREG_SEQU_ADDRESS 0x3c4
+#define VGAREG_SEQU_DATA 0x3c5
+
+#define VGAREG_PEL_MASK 0x3c6
+#define VGAREG_DAC_STATE 0x3c7
+#define VGAREG_DAC_READ_ADDRESS 0x3c7
+#define VGAREG_DAC_WRITE_ADDRESS 0x3c8
+#define VGAREG_DAC_DATA 0x3c9
+
+#define VGAREG_READ_FEATURE_CTL 0x3ca
+#define VGAREG_READ_MISC_OUTPUT 0x3cc
+
+#define VGAREG_GRDC_ADDRESS 0x3ce
+#define VGAREG_GRDC_DATA 0x3cf
+
+#define VGAREG_MDA_CRTC_ADDRESS 0x3b4
+#define VGAREG_MDA_CRTC_DATA 0x3b5
+#define VGAREG_VGA_CRTC_ADDRESS 0x3d4
+#define VGAREG_VGA_CRTC_DATA 0x3d5
+
+#define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba
+#define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da
+#define VGAREG_ACTL_RESET 0x3da
+
+#define VGAREG_MDA_MODECTL 0x3b8
+#define VGAREG_CGA_MODECTL 0x3d8
+#define VGAREG_CGA_PALETTE 0x3d9
+
+/* Video memory */
+#define VGAMEM_GRAPH 0xA000
+#define VGAMEM_CTEXT 0xB800
+#define VGAMEM_MTEXT 0xB000
+
+/*
+ *
+ * Tables of default values for each mode
+ *
+ */
+#define MODE_MAX 15
+#define TEXT 0x00
+#define GRAPH 0x01
+
+#define CTEXT 0x00
+#define MTEXT 0x01
+#define CGA 0x02
+#define PLANAR1 0x03
+#define PLANAR4 0x04
+#define LINEAR8 0x05
+
+// for SVGA
+#define LINEAR15 0x10
+#define LINEAR16 0x11
+#define LINEAR24 0x12
+#define LINEAR32 0x13
+
+typedef struct
+{Bit8u svgamode;
+ Bit8u class; /* TEXT, GRAPH */
+ Bit8u memmodel; /* CTEXT,MTEXT,CGA,PL1,PL2,PL4,P8,P15,P16,P24,P32 */
+ Bit8u pixbits;
+ Bit16u sstart;
+ Bit8u pelmask;
+ Bit8u dacmodel; /* 0 1 2 3 */
+} VGAMODES;
+
+static VGAMODES vga_modes[MODE_MAX+1]=
+{//mode class model bits sstart pelm dac
+ {0x00, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x01, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x02, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x03, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x04, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01},
+ {0x05, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01},
+ {0x06, GRAPH, CGA, 1, 0xB800, 0xFF, 0x01},
+ {0x07, TEXT, MTEXT, 4, 0xB000, 0xFF, 0x00},
+ {0x0D, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01},
+ {0x0E, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01},
+ {0x0F, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x00},
+ {0x10, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02},
+ {0x11, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x02},
+ {0x12, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02},
+ {0x13, GRAPH, LINEAR8, 8, 0xA000, 0xFF, 0x03},
+ {0x6A, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}
+};
+
+/* convert index in vga_modes[] to index in video_param_table[] */
+static Bit8u line_to_vpti[MODE_MAX+1]={
+ 0x17, 0x17, 0x18, 0x18, 0x04, 0x05, 0x06, 0x07,
+ 0x0d, 0x0e, 0x11, 0x12, 0x1a, 0x1b, 0x1c, 0x1d,
+};
+
+/* Default Palette */
+#define DAC_MAX_MODEL 3
+
+static Bit8u dac_regs[DAC_MAX_MODEL+1]=
+{0x3f,0x3f,0x3f,0xff};
+
+/* standard BIOS Video Parameter Table */
+typedef struct {
+ Bit8u twidth;
+ Bit8u theightm1;
+ Bit8u cheight;
+ Bit8u slength_l;
+ Bit8u slength_h;
+ Bit8u sequ_regs[4];
+ Bit8u miscreg;
+ Bit8u crtc_regs[25];
+ Bit8u actl_regs[20];
+ Bit8u grdc_regs[9];
+} VideoParamTableEntry;
+
+static VideoParamTableEntry video_param_table[30] = {
+{
+ /* index=0x00 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x01 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x02 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x03 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x04 vga mode 0x04 */
+ 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */
+ 0x09, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2,
+ 0xff, /* crtc_regs */
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x03, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x05 vga mode 0x05 */
+ 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */
+ 0x09, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2,
+ 0xff, /* crtc_regs */
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x03, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x06 vga mode 0x06 */
+ 80, 24, 8, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x01, 0x01, 0x00, 0x06, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2,
+ 0xff, /* crtc_regs */
+ 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x01, 0x00, 0x01, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x07 vga mode 0x07 */
+ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x66, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x08 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x09 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0a no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0b no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0c no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0d vga mode 0x0d */
+ 40, 24, 8, 0x00, 0x20, /* tw, th-1, ch, slength */
+ 0x09, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x0e vga mode 0x0e */
+ 80, 24, 8, 0x00, 0x40, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x0f no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x10 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x11 vga mode 0x0f */
+ 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xa3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x12 vga mode 0x10 */
+ 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xa3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x13 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x14 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x15 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x16 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x17 vga mode 0x01 */
+ 40, 24, 16, 0x00, 0x08, /* tw, th-1, ch, slength */
+ 0x08, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x67, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x18 vga mode 0x03 */
+ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x67, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x19 vga mode 0x07 */
+ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x66, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1a vga mode 0x11 */
+ 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xe3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f,
+ 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1b vga mode 0x12 */
+ 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xe3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1c vga mode 0x13 */
+ 40, 24, 8, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x0e, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x41, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1d vga mode 0x6a */
+ 100, 36, 16, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xe3, /* miscreg */
+ 0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x59, 0x8d, 0x57, 0x32, 0x00, 0x57, 0x73, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+};
+
+/* Mono */
+static Bit8u palette0[63+1][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f
+};
+
+static Bit8u palette1[63+1][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f
+};
+
+static Bit8u palette2[63+1][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f, 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f,
+ 0x00,0x15,0x00, 0x00,0x15,0x2a, 0x00,0x3f,0x00, 0x00,0x3f,0x2a, 0x2a,0x15,0x00, 0x2a,0x15,0x2a, 0x2a,0x3f,0x00, 0x2a,0x3f,0x2a,
+ 0x00,0x15,0x15, 0x00,0x15,0x3f, 0x00,0x3f,0x15, 0x00,0x3f,0x3f, 0x2a,0x15,0x15, 0x2a,0x15,0x3f, 0x2a,0x3f,0x15, 0x2a,0x3f,0x3f,
+ 0x15,0x00,0x00, 0x15,0x00,0x2a, 0x15,0x2a,0x00, 0x15,0x2a,0x2a, 0x3f,0x00,0x00, 0x3f,0x00,0x2a, 0x3f,0x2a,0x00, 0x3f,0x2a,0x2a,
+ 0x15,0x00,0x15, 0x15,0x00,0x3f, 0x15,0x2a,0x15, 0x15,0x2a,0x3f, 0x3f,0x00,0x15, 0x3f,0x00,0x3f, 0x3f,0x2a,0x15, 0x3f,0x2a,0x3f,
+ 0x15,0x15,0x00, 0x15,0x15,0x2a, 0x15,0x3f,0x00, 0x15,0x3f,0x2a, 0x3f,0x15,0x00, 0x3f,0x15,0x2a, 0x3f,0x3f,0x00, 0x3f,0x3f,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f
+};
+
+static Bit8u palette3[256][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x00, 0x05,0x05,0x05, 0x08,0x08,0x08, 0x0b,0x0b,0x0b, 0x0e,0x0e,0x0e, 0x11,0x11,0x11, 0x14,0x14,0x14, 0x18,0x18,0x18,
+ 0x1c,0x1c,0x1c, 0x20,0x20,0x20, 0x24,0x24,0x24, 0x28,0x28,0x28, 0x2d,0x2d,0x2d, 0x32,0x32,0x32, 0x38,0x38,0x38, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x3f, 0x10,0x00,0x3f, 0x1f,0x00,0x3f, 0x2f,0x00,0x3f, 0x3f,0x00,0x3f, 0x3f,0x00,0x2f, 0x3f,0x00,0x1f, 0x3f,0x00,0x10,
+ 0x3f,0x00,0x00, 0x3f,0x10,0x00, 0x3f,0x1f,0x00, 0x3f,0x2f,0x00, 0x3f,0x3f,0x00, 0x2f,0x3f,0x00, 0x1f,0x3f,0x00, 0x10,0x3f,0x00,
+ 0x00,0x3f,0x00, 0x00,0x3f,0x10, 0x00,0x3f,0x1f, 0x00,0x3f,0x2f, 0x00,0x3f,0x3f, 0x00,0x2f,0x3f, 0x00,0x1f,0x3f, 0x00,0x10,0x3f,
+ 0x1f,0x1f,0x3f, 0x27,0x1f,0x3f, 0x2f,0x1f,0x3f, 0x37,0x1f,0x3f, 0x3f,0x1f,0x3f, 0x3f,0x1f,0x37, 0x3f,0x1f,0x2f, 0x3f,0x1f,0x27,
+
+ 0x3f,0x1f,0x1f, 0x3f,0x27,0x1f, 0x3f,0x2f,0x1f, 0x3f,0x37,0x1f, 0x3f,0x3f,0x1f, 0x37,0x3f,0x1f, 0x2f,0x3f,0x1f, 0x27,0x3f,0x1f,
+ 0x1f,0x3f,0x1f, 0x1f,0x3f,0x27, 0x1f,0x3f,0x2f, 0x1f,0x3f,0x37, 0x1f,0x3f,0x3f, 0x1f,0x37,0x3f, 0x1f,0x2f,0x3f, 0x1f,0x27,0x3f,
+ 0x2d,0x2d,0x3f, 0x31,0x2d,0x3f, 0x36,0x2d,0x3f, 0x3a,0x2d,0x3f, 0x3f,0x2d,0x3f, 0x3f,0x2d,0x3a, 0x3f,0x2d,0x36, 0x3f,0x2d,0x31,
+ 0x3f,0x2d,0x2d, 0x3f,0x31,0x2d, 0x3f,0x36,0x2d, 0x3f,0x3a,0x2d, 0x3f,0x3f,0x2d, 0x3a,0x3f,0x2d, 0x36,0x3f,0x2d, 0x31,0x3f,0x2d,
+ 0x2d,0x3f,0x2d, 0x2d,0x3f,0x31, 0x2d,0x3f,0x36, 0x2d,0x3f,0x3a, 0x2d,0x3f,0x3f, 0x2d,0x3a,0x3f, 0x2d,0x36,0x3f, 0x2d,0x31,0x3f,
+ 0x00,0x00,0x1c, 0x07,0x00,0x1c, 0x0e,0x00,0x1c, 0x15,0x00,0x1c, 0x1c,0x00,0x1c, 0x1c,0x00,0x15, 0x1c,0x00,0x0e, 0x1c,0x00,0x07,
+ 0x1c,0x00,0x00, 0x1c,0x07,0x00, 0x1c,0x0e,0x00, 0x1c,0x15,0x00, 0x1c,0x1c,0x00, 0x15,0x1c,0x00, 0x0e,0x1c,0x00, 0x07,0x1c,0x00,
+ 0x00,0x1c,0x00, 0x00,0x1c,0x07, 0x00,0x1c,0x0e, 0x00,0x1c,0x15, 0x00,0x1c,0x1c, 0x00,0x15,0x1c, 0x00,0x0e,0x1c, 0x00,0x07,0x1c,
+
+ 0x0e,0x0e,0x1c, 0x11,0x0e,0x1c, 0x15,0x0e,0x1c, 0x18,0x0e,0x1c, 0x1c,0x0e,0x1c, 0x1c,0x0e,0x18, 0x1c,0x0e,0x15, 0x1c,0x0e,0x11,
+ 0x1c,0x0e,0x0e, 0x1c,0x11,0x0e, 0x1c,0x15,0x0e, 0x1c,0x18,0x0e, 0x1c,0x1c,0x0e, 0x18,0x1c,0x0e, 0x15,0x1c,0x0e, 0x11,0x1c,0x0e,
+ 0x0e,0x1c,0x0e, 0x0e,0x1c,0x11, 0x0e,0x1c,0x15, 0x0e,0x1c,0x18, 0x0e,0x1c,0x1c, 0x0e,0x18,0x1c, 0x0e,0x15,0x1c, 0x0e,0x11,0x1c,
+ 0x14,0x14,0x1c, 0x16,0x14,0x1c, 0x18,0x14,0x1c, 0x1a,0x14,0x1c, 0x1c,0x14,0x1c, 0x1c,0x14,0x1a, 0x1c,0x14,0x18, 0x1c,0x14,0x16,
+ 0x1c,0x14,0x14, 0x1c,0x16,0x14, 0x1c,0x18,0x14, 0x1c,0x1a,0x14, 0x1c,0x1c,0x14, 0x1a,0x1c,0x14, 0x18,0x1c,0x14, 0x16,0x1c,0x14,
+ 0x14,0x1c,0x14, 0x14,0x1c,0x16, 0x14,0x1c,0x18, 0x14,0x1c,0x1a, 0x14,0x1c,0x1c, 0x14,0x1a,0x1c, 0x14,0x18,0x1c, 0x14,0x16,0x1c,
+ 0x00,0x00,0x10, 0x04,0x00,0x10, 0x08,0x00,0x10, 0x0c,0x00,0x10, 0x10,0x00,0x10, 0x10,0x00,0x0c, 0x10,0x00,0x08, 0x10,0x00,0x04,
+ 0x10,0x00,0x00, 0x10,0x04,0x00, 0x10,0x08,0x00, 0x10,0x0c,0x00, 0x10,0x10,0x00, 0x0c,0x10,0x00, 0x08,0x10,0x00, 0x04,0x10,0x00,
+
+ 0x00,0x10,0x00, 0x00,0x10,0x04, 0x00,0x10,0x08, 0x00,0x10,0x0c, 0x00,0x10,0x10, 0x00,0x0c,0x10, 0x00,0x08,0x10, 0x00,0x04,0x10,
+ 0x08,0x08,0x10, 0x0a,0x08,0x10, 0x0c,0x08,0x10, 0x0e,0x08,0x10, 0x10,0x08,0x10, 0x10,0x08,0x0e, 0x10,0x08,0x0c, 0x10,0x08,0x0a,
+ 0x10,0x08,0x08, 0x10,0x0a,0x08, 0x10,0x0c,0x08, 0x10,0x0e,0x08, 0x10,0x10,0x08, 0x0e,0x10,0x08, 0x0c,0x10,0x08, 0x0a,0x10,0x08,
+ 0x08,0x10,0x08, 0x08,0x10,0x0a, 0x08,0x10,0x0c, 0x08,0x10,0x0e, 0x08,0x10,0x10, 0x08,0x0e,0x10, 0x08,0x0c,0x10, 0x08,0x0a,0x10,
+ 0x0b,0x0b,0x10, 0x0c,0x0b,0x10, 0x0d,0x0b,0x10, 0x0f,0x0b,0x10, 0x10,0x0b,0x10, 0x10,0x0b,0x0f, 0x10,0x0b,0x0d, 0x10,0x0b,0x0c,
+ 0x10,0x0b,0x0b, 0x10,0x0c,0x0b, 0x10,0x0d,0x0b, 0x10,0x0f,0x0b, 0x10,0x10,0x0b, 0x0f,0x10,0x0b, 0x0d,0x10,0x0b, 0x0c,0x10,0x0b,
+ 0x0b,0x10,0x0b, 0x0b,0x10,0x0c, 0x0b,0x10,0x0d, 0x0b,0x10,0x0f, 0x0b,0x10,0x10, 0x0b,0x0f,0x10, 0x0b,0x0d,0x10, 0x0b,0x0c,0x10,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00
+};
+
+static Bit8u static_functionality[0x10]=
+{
+ /* 0 */ 0xff, // All modes supported #1
+ /* 1 */ 0xe0, // All modes supported #2
+ /* 2 */ 0x0f, // All modes supported #3
+ /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved
+ /* 7 */ 0x07, // 200, 350, 400 scan lines
+ /* 8 */ 0x02, // mamimum number of visible charsets in text mode
+ /* 9 */ 0x08, // total number of charset blocks in text mode
+ /* a */ 0xe7, // Change to add new functions
+ /* b */ 0x0c, // Change to add new functions
+ /* c */ 0x00, // reserved
+ /* d */ 0x00, // reserved
+ /* e */ 0x00, // Change to add new functions
+ /* f */ 0x00 // reserved
+};
diff --git a/linux-user/main.c b/linux-user/main.c
index 238165f45..cc9f53dea 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -35,6 +35,7 @@
static const char *interp_prefix = CONFIG_QEMU_PREFIX;
const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
+const char *cpu_vendor_string = NULL;
#if defined(__i386__) && !defined(CONFIG_STATIC)
/* Force usage of an ELF interpreter even if it is an ELF shared
diff --git a/monitor.c b/monitor.c
index 8cec8c9e1..6cbab46b8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -40,6 +40,8 @@
#include "migration.h"
#include "kvm.h"
+#include "qemu-kvm.h"
+
//#define DEBUG
//#define DEBUG_COMPLETION
@@ -297,6 +299,9 @@ static CPUState *mon_get_cpu(void)
if (!mon_cpu) {
mon_set_cpu(0);
}
+
+ kvm_save_registers(mon_cpu);
+
return mon_cpu;
}
@@ -323,6 +328,7 @@ static void do_info_cpus(void)
mon_get_cpu();
for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ kvm_save_registers(env);
term_printf("%c CPU #%d:",
(env == mon_cpu) ? '*' : ' ',
env->cpu_index);
@@ -337,6 +343,7 @@ static void do_info_cpus(void)
#endif
if (env->halted)
term_printf(" (halted)");
+ term_printf(" thread_id=%d", env->thread_id);
term_printf("\n");
}
}
@@ -347,6 +354,23 @@ static void do_cpu_set(int index)
term_printf("Invalid CPU index\n");
}
+static void do_cpu_set_nr(int value, const char *status)
+{
+ int state;
+
+ if (!strcmp(status, "online"))
+ state = 1;
+ else if (!strcmp(status, "offline"))
+ state = 0;
+ else {
+ term_printf("invalid status: %s\n", status);
+ return;
+ }
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+ qemu_system_cpu_hot_add(value, state);
+#endif
+}
+
static void do_info_jit(void)
{
dump_exec_info(NULL, monitor_fprintf);
@@ -1277,7 +1301,7 @@ static void do_info_kqemu(void)
static void do_info_kvm(void)
{
-#ifdef CONFIG_KVM
+#if defined(USE_KVM) || defined(CONFIG_KVM)
term_printf("kvm support: ");
if (kvm_enabled())
term_printf("enabled\n");
@@ -1395,7 +1419,10 @@ static void do_inject_nmi(int cpu_index)
for (env = first_cpu; env != NULL; env = env->next_cpu)
if (env->cpu_index == cpu_index) {
- cpu_interrupt(env, CPU_INTERRUPT_NMI);
+ if (kvm_enabled())
+ kvm_inject_interrupt(env, CPU_INTERRUPT_NMI);
+ else
+ cpu_interrupt(env, CPU_INTERRUPT_NMI);
break;
}
}
@@ -1516,6 +1543,16 @@ static const term_cmd_t term_cmds[] = {
"value", "set maximum speed (in bytes) for migrations" },
{ "balloon", "i", do_balloon,
"target", "request VM to change it's memory allocation (in MB)" },
+ { "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" },
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+ { "drive_add", "iss", drive_hot_add, "pcibus pcidevfn [file=file][,if=type][,bus=n]\n"
+ "[,unit=m][,media=d][index=i]\n"
+ "[,cyls=c,heads=h,secs=s[,trans=t]]\n"
+ "[snapshot=on|off][,cache=on|off]",
+ "add drive to PCI storage controller" },
+ { "pci_add", "iss", device_hot_add, "bus nic|storage|host [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]... [host=02:00.0[,name=string][,dma=none]", "hot-add PCI device" },
+ { "pci_del", "ii", device_hot_remove, "bus slot-number", "hot remove PCI device" },
+#endif
{ NULL, NULL, },
};
diff --git a/net.c b/net.c
index c49abef52..ee6619372 100644
--- a/net.c
+++ b/net.c
@@ -120,6 +120,8 @@
#define memalign(align, size) malloc(size)
#endif
+// FIXME: #include "qemu-kvm.h"
+
static VLANState *first_vlan;
/***********************************************************/
@@ -345,10 +347,11 @@ int qemu_can_send_packet(VLANClientState *vc1)
return 0;
}
-void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+int qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
{
VLANState *vlan = vc1->vlan;
VLANClientState *vc;
+ int ret = -EAGAIN;
#ifdef DEBUG_NET
printf("vlan %d send:\n", vlan->id);
@@ -356,9 +359,13 @@ void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
#endif
for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
if (vc != vc1) {
- vc->fd_read(vc->opaque, buf, size);
+ if (!vc->fd_can_read || vc->fd_can_read(vc->opaque)) {
+ vc->fd_read(vc->opaque, buf, size);
+ ret = 0;
+ }
}
}
+ return ret;
}
static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
@@ -604,12 +611,36 @@ void do_info_slirp(void)
#endif /* CONFIG_SLIRP */
-#if !defined(_WIN32)
+#ifdef _WIN32
+
+int tap_has_vnet_hdr(void *opaque)
+{
+ return 0;
+}
+
+void tap_using_vnet_hdr(void *opaque, int using_vnet_hdr)
+{
+}
+
+#else /* !defined(_WIN32) */
+
+/* Maximum GSO packet size (64k) plus plenty of room for
+ * the ethernet and virtio_net headers
+ */
+#define TAP_BUFSIZE (4096 + 65536)
+
+#ifdef IFF_VNET_HDR
+#include <linux/virtio_net.h>
+#endif
typedef struct TAPState {
VLANClientState *vc;
int fd;
char down_script[1024];
+ char buf[TAP_BUFSIZE];
+ int size;
+ unsigned int has_vnet_hdr : 1;
+ unsigned int using_vnet_hdr : 1;
} TAPState;
#ifdef HAVE_IOVEC
@@ -629,40 +660,171 @@ static ssize_t tap_receive_iov(void *opaque, const struct iovec *iov,
static void tap_receive(void *opaque, const uint8_t *buf, int size)
{
+ struct iovec iov[2];
+ int i = 0;
+
+#ifdef IFF_VNET_HDR
TAPState *s = opaque;
- int ret;
- for(;;) {
- ret = write(s->fd, buf, size);
- if (ret < 0 && (errno == EINTR || errno == EAGAIN)) {
- } else {
- break;
- }
+ struct virtio_net_hdr hdr = { 0, };
+
+ if (s->has_vnet_hdr && !s->using_vnet_hdr) {
+ iov[i].iov_base = &hdr;
+ iov[i].iov_len = sizeof(hdr);
+ i++;
+ }
+#endif
+
+ iov[i].iov_base = (char *) buf;
+ iov[i].iov_len = size;
+ i++;
+
+ tap_receive_iov(opaque, iov, i);
+}
+
+static int tap_can_send(void *opaque)
+{
+ TAPState *s = opaque;
+ VLANClientState *vc;
+ int can_receive = 0;
+
+ /* Check to see if any of our clients can receive a packet */
+ for (vc = s->vc->vlan->first_client; vc; vc = vc->next) {
+ /* Skip ourselves */
+ if (vc == s->vc)
+ continue;
+
+ if (!vc->fd_can_read) {
+ /* no fd_can_read handler, they always can receive */
+ can_receive = 1;
+ } else
+ can_receive = vc->fd_can_read(vc->opaque);
+
+ /* Once someone can receive, we try to send a packet */
+ if (can_receive)
+ break;
+ }
+
+ return can_receive;
+}
+
+static int tap_send_packet(TAPState *s)
+{
+ uint8_t *buf = s->buf;
+ int size = s->size;
+
+#ifdef IFF_VNET_HDR
+ if (s->has_vnet_hdr && !s->using_vnet_hdr) {
+ buf += sizeof(struct virtio_net_hdr);
+ size -= sizeof(struct virtio_net_hdr);
}
+#endif
+
+ return qemu_send_packet(s->vc, buf, size);
}
static void tap_send(void *opaque)
{
TAPState *s = opaque;
- uint8_t buf[4096];
- int size;
+ /* First try to send any buffered packet */
+ if (s->size > 0) {
+ int err;
+
+ /* If noone can receive the packet, buffer it */
+ err = tap_send_packet(s);
+ if (err == -EAGAIN)
+ return;
+ }
+
+ /* Read packets until we hit EAGAIN */
+ do {
#ifdef __sun__
- struct strbuf sbuf;
- int f = 0;
- sbuf.maxlen = sizeof(buf);
- sbuf.buf = buf;
- size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
+ struct strbuf sbuf;
+ int f = 0;
+ sbuf.maxlen = sizeof(s->buf);
+ sbuf.buf = s->buf;
+ s->size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
#else
- size = read(s->fd, buf, sizeof(buf));
+ // FIXME: kvm_sleep_begin();
+ s->size = read(s->fd, s->buf, sizeof(s->buf));
+ // FIXME: kvm_sleep_end();
#endif
- if (size > 0) {
- qemu_send_packet(s->vc, buf, size);
+
+ if (s->size == -1 && errno == EINTR)
+ continue;
+
+ if (s->size > 0) {
+ int err;
+
+ /* If noone can receive the packet, buffer it */
+ err = tap_send_packet(s);
+ if (err == -EAGAIN)
+ break;
+ }
+ } while (s->size > 0);
+}
+
+int tap_has_vnet_hdr(void *opaque)
+{
+ VLANClientState *vc = opaque;
+ TAPState *s = vc->opaque;
+
+ return s ? s->has_vnet_hdr : 0;
+}
+
+void tap_using_vnet_hdr(void *opaque, int using_vnet_hdr)
+{
+ VLANClientState *vc = opaque;
+ TAPState *s = vc->opaque;
+
+ if (!s || !s->has_vnet_hdr)
+ return;
+
+ s->using_vnet_hdr = using_vnet_hdr != 0;
+}
+
+static int tap_probe_vnet_hdr(int fd)
+{
+#if defined(TUNGETIFF) && defined(IFF_VNET_HDR)
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
+ fprintf(stderr, "TUNGETIFF ioctl() failed: %s\n", strerror(errno));
+ return 0;
}
+
+ return ifr.ifr_flags & IFF_VNET_HDR;
+#else
+ return 0;
+#endif
}
+#ifdef TUNSETOFFLOAD
+static void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6,
+ int ecn)
+{
+ TAPState *s = vc->opaque;
+ unsigned int offload = 0;
+
+ if (csum) {
+ offload |= TUN_F_CSUM;
+ if (tso4)
+ offload |= TUN_F_TSO4;
+ if (tso6)
+ offload |= TUN_F_TSO6;
+ if ((tso4 || tso6) && ecn)
+ offload |= TUN_F_TSO_ECN;
+ }
+
+ if (ioctl(s->fd, TUNSETOFFLOAD, offload) != 0)
+ fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n",
+ strerror(errno));
+}
+#endif /* TUNSETOFFLOAD */
+
/* fd support */
-static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
+static TAPState *net_tap_fd_init(VLANState *vlan, int fd, int vnet_hdr)
{
TAPState *s;
@@ -670,11 +832,15 @@ static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
if (!s)
return NULL;
s->fd = fd;
+ s->has_vnet_hdr = vnet_hdr != 0;
s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
#ifdef HAVE_IOVEC
s->vc->fd_readv = tap_receive_iov;
#endif
- qemu_set_fd_handler(s->fd, tap_send, NULL, s);
+#ifdef TUNSETOFFLOAD
+ s->vc->set_offload = tap_set_offload;
+#endif
+ qemu_set_fd_handler2(s->fd, tap_can_send, tap_send, NULL, s);
snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
return s;
}
@@ -822,7 +988,7 @@ int tap_alloc(char *dev, size_t dev_size)
return tap_fd;
}
-static int tap_open(char *ifname, int ifname_size)
+static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
{
char dev[10]="";
int fd;
@@ -841,7 +1007,7 @@ static int tap_open(char *ifname, int ifname_size)
return -1;
}
#else
-static int tap_open(char *ifname, int ifname_size)
+static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
{
struct ifreq ifr;
int fd, ret;
@@ -853,6 +1019,19 @@ static int tap_open(char *ifname, int ifname_size)
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+#if defined(TUNGETFEATURES) && defined(IFF_VNET_HDR)
+ {
+ unsigned int features;
+
+ if (ioctl(fd, TUNGETFEATURES, &features) == 0 &&
+ features & IFF_VNET_HDR) {
+ *vnet_hdr = 1;
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ }
+ }
+#endif
+
if (ifname[0] != '\0')
pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
else
@@ -910,13 +1089,15 @@ static int net_tap_init(VLANState *vlan, const char *ifname1,
{
TAPState *s;
int fd;
+ int vnet_hdr;
char ifname[128];
if (ifname1 != NULL)
pstrcpy(ifname, sizeof(ifname), ifname1);
else
ifname[0] = '\0';
- TFR(fd = tap_open(ifname, sizeof(ifname)));
+ vnet_hdr = 0;
+ TFR(fd = tap_open(ifname, sizeof(ifname), &vnet_hdr));
if (fd < 0)
return -1;
@@ -926,9 +1107,10 @@ static int net_tap_init(VLANState *vlan, const char *ifname1,
if (launch_script(setup_script, ifname, fd))
return -1;
}
- s = net_tap_fd_init(vlan, fd);
+ s = net_tap_fd_init(vlan, fd, vnet_hdr);
if (!s)
return -1;
+
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
"tap: ifname=%s setup_script=%s", ifname, setup_script);
if (down_script && strcmp(down_script, "no"))
@@ -1512,7 +1694,7 @@ int net_client_init(const char *device, const char *p)
fd = strtol(buf, NULL, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
ret = -1;
- if (net_tap_fd_init(vlan, fd))
+ if (net_tap_fd_init(vlan, fd, tap_probe_vnet_hdr(fd)))
ret = 0;
} else {
if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
@@ -1582,6 +1764,14 @@ int net_client_init(const char *device, const char *p)
return ret;
}
+void net_client_uninit(NICInfo *nd)
+{
+ nd->vlan->nb_guest_devs--; /* XXX: free vlan on last reference */
+ nb_nics--;
+ nd->used = 0;
+ free((void *)nd->model);
+}
+
int net_client_parse(const char *str)
{
const char *p;
diff --git a/net.h b/net.h
index 31c7a308c..91324dfc9 100644
--- a/net.h
+++ b/net.h
@@ -9,12 +9,15 @@ typedef ssize_t (IOReadvHandler)(void *, const struct iovec *, int);
typedef struct VLANClientState VLANClientState;
+typedef void (SetOffload)(VLANClientState *, int, int, int, int);
+
struct VLANClientState {
IOReadHandler *fd_read;
IOReadvHandler *fd_readv;
/* Packets may still be sent if this returns zero. It's used to
rate-limit the slirp code. */
IOCanRWHandler *fd_can_read;
+ SetOffload *set_offload;
void *opaque;
struct VLANClientState *next;
struct VLANState *vlan;
@@ -37,11 +40,14 @@ void qemu_del_vlan_client(VLANClientState *vc);
int qemu_can_send_packet(VLANClientState *vc);
ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov,
int iovcnt);
-void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+int qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
void qemu_handler_true(void *opaque);
void do_info_network(void);
+int tap_has_vnet_hdr(void *opaque);
+void tap_using_vnet_hdr(void *opaque, int using_vnet_hdr);
+
/* NIC info */
#define MAX_NICS 8
@@ -50,6 +56,8 @@ struct NICInfo {
uint8_t macaddr[6];
const char *model;
VLANState *vlan;
+ int devfn;
+ int used;
};
extern int nb_nics;
@@ -78,6 +86,7 @@ void net_checksum_calculate(uint8_t *data, int length);
/* from net.c */
int net_client_init(const char *device, const char *p);
+void net_client_uninit(NICInfo *nd);
int net_client_parse(const char *str);
void net_slirp_smb(const char *exported_dir);
void net_slirp_redir(const char *redir_str);
diff --git a/osdep.c b/osdep.c
index 824120f7d..6e751c52d 100644
--- a/osdep.c
+++ b/osdep.c
@@ -200,7 +200,11 @@ void *qemu_vmalloc(size_t size)
if (kqemu_allowed)
return kqemu_vmalloc(size);
#endif
+#ifndef __ia64__
return qemu_memalign(getpagesize(), size);
+#else
+ return qemu_memalign(65536, size);
+#endif
}
void qemu_vfree(void *ptr)
diff --git a/pc-bios/Makefile b/pc-bios/Makefile
index d9f434ae5..611790343 100644
--- a/pc-bios/Makefile
+++ b/pc-bios/Makefile
@@ -12,5 +12,8 @@ all: $(TARGETS)
%.o: %.S
$(CC) $(DEFINES) -c -o $@ $<
+%.dtb: %.dts
+ dtc -O dtb -I dts -o $@ $<
+
clean:
- rm -f $(TARGETS) *.o *~
+ rm -f $(TARGETS) *.o *~ *.dtb
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 5120192de..35fffecf8 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differ
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 4fa8f99f7..7007bf093 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 fa6f815fc..c38c62ae3 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 d3df63ea0..5f75b2e7a 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -105,6 +105,8 @@ 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 qemu_fls(int i);
+int hex2bin(char ch);
+char *urldecode(const char *ptr);
#define qemu_isalnum(c) isalnum((unsigned char)(c))
#define qemu_isalpha(c) isalpha((unsigned char)(c))
@@ -176,7 +178,12 @@ struct pcmcia_card_s;
void cpu_save(QEMUFile *f, void *opaque);
int cpu_load(QEMUFile *f, void *opaque, int version_id);
-/* Force QEMU to stop what it's doing and service IO */
-void qemu_service_io(void);
+/* work queue */
+struct qemu_work_item {
+ struct qemu_work_item *next;
+ void (*func)(void *data);
+ void *data;
+ int done;
+};
#endif
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 698e0d57b..e004a2619 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -274,6 +274,8 @@ These options have the same definition as they have in @option{-hdachs}.
Specify which disk @var{format} will be used rather than detecting
the format. Can be used to specifiy format=raw to avoid interpreting
an untrusted format header.
+@item boot=@var{boot}
+@var{boot} if "on" enables extboot for a given drive so it can be used as a boot drive.
@end table
By default, writethrough caching is used for all block device. This means that
@@ -326,6 +328,12 @@ You can connect a SCSI disk with unit ID 6 on the bus #0:
qemu -drive file=file,if=scsi,bus=0,unit=6
@end example
+To boot from a SCSI disk, one would use:
+
+@example
+qemu -drive file=file,if=scsi,boot=on
+@end example
+
Instead of @option{-fda}, @option{-fdb}, you can use:
@example
qemu -drive file=file,index=0,if=floppy
@@ -608,7 +616,7 @@ Network options:
@item -net nic[,vlan=@var{n}][,macaddr=@var{addr}][,model=@var{type}]
Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n}
-= 0 is the default). The NIC is an ne2k_pci by default on the PC
+= 0 is the default). The NIC is an rtl8139 by default on the PC
target. Optionally, the MAC address can be changed. If no
@option{-net} option is specified, a single NIC is created.
Qemu can emulate several different models of network card.
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..f9e4b4b17
--- /dev/null
+++ b/qemu-kvm-ia64.c
@@ -0,0 +1,146 @@
+#include "config.h"
+#include "config-host.h"
+
+#include <string.h>
+
+#include "hw/hw.h"
+#include "qemu-kvm.h"
+#include <libkvm.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+
+
+extern kvm_context_t kvm_context;
+
+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, CPUState *env)
+{
+}
+
+void kvm_arch_post_kvm_run(void *opaque, CPUState *env)
+{
+}
+
+int kvm_arch_has_work(CPUState *env)
+{
+ return 1;
+}
+
+int kvm_arch_try_push_interrupts(void *opaque)
+{
+ return 1;
+}
+
+void kvm_arch_update_regs_for_sipi(CPUState *env)
+{
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+}
+
+void kvm_save_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ int r;
+ struct kvm_mp_state mp_state;
+
+ r = kvm_get_mpstate(kvm_context, env->cpu_index, &mp_state);
+ if (r < 0)
+ env->mp_state = -1;
+ else
+ env->mp_state = mp_state.mp_state;
+#endif
+}
+
+void kvm_load_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ struct kvm_mp_state mp_state = { .mp_state = env->mp_state };
+
+ /*
+ * -1 indicates that the host did not support GET_MP_STATE ioctl,
+ * so don't touch it.
+ */
+ if (env->mp_state != -1)
+ kvm_set_mpstate(kvm_context, env->cpu_index, &mp_state);
+#endif
+}
+
+void kvm_arch_cpu_reset(CPUState *env)
+{
+ if (kvm_irqchip_in_kernel(kvm_context)) {
+#ifdef KVM_CAP_MP_STATE
+ kvm_reset_mpstate(kvm_context, env->cpu_index);
+#endif
+ } else {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ }
+}
+
+void kvm_arch_do_ioperm(void *_data)
+{
+ struct ioperm_data *data = _data;
+ ioperm(data->start_port, data->num, data->turn_on);
+}
diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c
new file mode 100644
index 000000000..aa36be880
--- /dev/null
+++ b/qemu-kvm-x86.c
@@ -0,0 +1,900 @@
+/*
+ * qemu/kvm integration, x86 specific code
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+
+#include "config.h"
+#include "config-host.h"
+
+#include <string.h>
+#include "hw/hw.h"
+#include "gdbstub.h"
+#include <sys/io.h>
+
+#include "qemu-kvm.h"
+#include <libkvm.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <linux/kvm_para.h>
+
+#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;
+
+int kvm_qemu_create_memory_alias(uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys)
+{
+ return kvm_create_memory_alias(kvm_context, phys_start, len, target_phys);
+}
+
+int kvm_qemu_destroy_memory_alias(uint64_t phys_start)
+{
+ return kvm_destroy_memory_alias(kvm_context, phys_start);
+}
+
+int kvm_arch_qemu_create_context(void)
+{
+ int i;
+ struct utsname utsname;
+
+ uname(&utsname);
+ lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
+
+ if (kvm_shadow_memory)
+ kvm_set_shadow_pages(kvm_context, kvm_shadow_memory);
+
+ kvm_msr_list = kvm_get_msr_list(kvm_context);
+ if (!kvm_msr_list)
+ return -1;
+ for (i = 0; i < kvm_msr_list->nmsrs; ++i)
+ if (kvm_msr_list->indices[i] == MSR_STAR)
+ kvm_has_msr_star = 1;
+ return 0;
+}
+
+static void set_msr_entry(struct kvm_msr_entry *entry, uint32_t index,
+ uint64_t data)
+{
+ entry->index = index;
+ entry->data = data;
+}
+
+/* returns 0 on success, non-0 on failure */
+static int get_msr_entry(struct kvm_msr_entry *entry, CPUState *env)
+{
+ switch (entry->index) {
+ case MSR_IA32_SYSENTER_CS:
+ env->sysenter_cs = entry->data;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ env->sysenter_esp = entry->data;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ env->sysenter_eip = entry->data;
+ break;
+ case MSR_STAR:
+ env->star = entry->data;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_CSTAR:
+ env->cstar = entry->data;
+ break;
+ case MSR_KERNELGSBASE:
+ env->kernelgsbase = entry->data;
+ break;
+ case MSR_FMASK:
+ env->fmask = entry->data;
+ break;
+ case MSR_LSTAR:
+ env->lstar = entry->data;
+ break;
+#endif
+ case MSR_IA32_TSC:
+ env->tsc = entry->data;
+ break;
+ case MSR_VM_HSAVE_PA:
+ env->vm_hsave = entry->data;
+ break;
+ default:
+ printf("Warning unknown msr index 0x%x\n", entry->index);
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef TARGET_X86_64
+#define MSR_COUNT 9
+#else
+#define MSR_COUNT 5
+#endif
+
+static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs)
+{
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->type = 3;
+ lhs->present = 1;
+ lhs->dpl = 3;
+ lhs->db = 0;
+ lhs->s = 1;
+ lhs->l = 0;
+ lhs->g = 0;
+ lhs->avl = 0;
+ lhs->unusable = 0;
+}
+
+static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs)
+{
+ unsigned flags = rhs->flags;
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->type = (flags >> DESC_TYPE_SHIFT) & 15;
+ lhs->present = (flags & DESC_P_MASK) != 0;
+ lhs->dpl = rhs->selector & 3;
+ lhs->db = (flags >> DESC_B_SHIFT) & 1;
+ lhs->s = (flags & DESC_S_MASK) != 0;
+ lhs->l = (flags >> DESC_L_SHIFT) & 1;
+ lhs->g = (flags & DESC_G_MASK) != 0;
+ lhs->avl = (flags & DESC_AVL_MASK) != 0;
+ lhs->unusable = 0;
+}
+
+static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs)
+{
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->flags =
+ (rhs->type << DESC_TYPE_SHIFT)
+ | (rhs->present * DESC_P_MASK)
+ | (rhs->dpl << DESC_DPL_SHIFT)
+ | (rhs->db << DESC_B_SHIFT)
+ | (rhs->s * DESC_S_MASK)
+ | (rhs->l << DESC_L_SHIFT)
+ | (rhs->g * DESC_G_MASK)
+ | (rhs->avl * DESC_AVL_MASK);
+}
+
+void kvm_arch_load_regs(CPUState *env)
+{
+ struct kvm_regs regs;
+ struct kvm_fpu fpu;
+ struct kvm_sregs sregs;
+ struct kvm_msr_entry msrs[MSR_COUNT];
+ int rc, n, i;
+
+ regs.rax = env->regs[R_EAX];
+ regs.rbx = env->regs[R_EBX];
+ regs.rcx = env->regs[R_ECX];
+ regs.rdx = env->regs[R_EDX];
+ regs.rsi = env->regs[R_ESI];
+ regs.rdi = env->regs[R_EDI];
+ regs.rsp = env->regs[R_ESP];
+ regs.rbp = env->regs[R_EBP];
+#ifdef TARGET_X86_64
+ regs.r8 = env->regs[8];
+ regs.r9 = env->regs[9];
+ regs.r10 = env->regs[10];
+ regs.r11 = env->regs[11];
+ regs.r12 = env->regs[12];
+ regs.r13 = env->regs[13];
+ regs.r14 = env->regs[14];
+ regs.r15 = env->regs[15];
+#endif
+
+ regs.rflags = env->eflags;
+ regs.rip = env->eip;
+
+ kvm_set_regs(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->interrupt_bitmap, sizeof(sregs.interrupt_bitmap));
+
+ if ((env->eflags & VM_MASK)) {
+ set_v8086_seg(&sregs.cs, &env->segs[R_CS]);
+ set_v8086_seg(&sregs.ds, &env->segs[R_DS]);
+ set_v8086_seg(&sregs.es, &env->segs[R_ES]);
+ set_v8086_seg(&sregs.fs, &env->segs[R_FS]);
+ set_v8086_seg(&sregs.gs, &env->segs[R_GS]);
+ set_v8086_seg(&sregs.ss, &env->segs[R_SS]);
+ } else {
+ set_seg(&sregs.cs, &env->segs[R_CS]);
+ set_seg(&sregs.ds, &env->segs[R_DS]);
+ set_seg(&sregs.es, &env->segs[R_ES]);
+ set_seg(&sregs.fs, &env->segs[R_FS]);
+ set_seg(&sregs.gs, &env->segs[R_GS]);
+ set_seg(&sregs.ss, &env->segs[R_SS]);
+
+ if (env->cr[0] & CR0_PE_MASK) {
+ /* force ss cpl to cs cpl */
+ sregs.ss.selector = (sregs.ss.selector & ~3) |
+ (sregs.cs.selector & 3);
+ sregs.ss.dpl = sregs.ss.selector & 3;
+ }
+ }
+
+ set_seg(&sregs.tr, &env->tr);
+ set_seg(&sregs.ldt, &env->ldt);
+
+ sregs.idt.limit = env->idt.limit;
+ sregs.idt.base = env->idt.base;
+ sregs.gdt.limit = env->gdt.limit;
+ sregs.gdt.base = env->gdt.base;
+
+ sregs.cr0 = env->cr[0];
+ sregs.cr2 = env->cr[2];
+ sregs.cr3 = env->cr[3];
+ sregs.cr4 = env->cr[4];
+
+ sregs.cr8 = cpu_get_apic_tpr(env);
+ sregs.apic_base = cpu_get_apic_base(env);
+
+ sregs.efer = env->efer;
+
+ kvm_set_sregs(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);
+ set_msr_entry(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave);
+#ifdef TARGET_X86_64
+ if (lm_capable_kernel) {
+ set_msr_entry(&msrs[n++], MSR_CSTAR, env->cstar);
+ set_msr_entry(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase);
+ set_msr_entry(&msrs[n++], MSR_FMASK, env->fmask);
+ set_msr_entry(&msrs[n++], MSR_LSTAR , env->lstar);
+ }
+#endif
+
+ rc = kvm_set_msrs(kvm_context, env->cpu_index, msrs, n);
+ if (rc == -1)
+ perror("kvm_set_msrs FAILED");
+}
+
+void kvm_save_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ int r;
+ struct kvm_mp_state mp_state;
+
+ r = kvm_get_mpstate(kvm_context, env->cpu_index, &mp_state);
+ if (r < 0)
+ env->mp_state = -1;
+ else
+ env->mp_state = mp_state.mp_state;
+#endif
+}
+
+void kvm_load_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ struct kvm_mp_state mp_state = { .mp_state = env->mp_state };
+
+ /*
+ * -1 indicates that the host did not support GET_MP_STATE ioctl,
+ * so don't touch it.
+ */
+ if (env->mp_state != -1)
+ kvm_set_mpstate(kvm_context, env->cpu_index, &mp_state);
+#endif
+}
+
+void kvm_arch_save_regs(CPUState *env)
+{
+ struct kvm_regs regs;
+ struct kvm_fpu fpu;
+ struct kvm_sregs sregs;
+ struct kvm_msr_entry msrs[MSR_COUNT];
+ uint32_t hflags;
+ uint32_t i, n, rc;
+
+ kvm_get_regs(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->interrupt_bitmap, sregs.interrupt_bitmap, sizeof(env->interrupt_bitmap));
+
+ get_seg(&env->segs[R_CS], &sregs.cs);
+ get_seg(&env->segs[R_DS], &sregs.ds);
+ get_seg(&env->segs[R_ES], &sregs.es);
+ get_seg(&env->segs[R_FS], &sregs.fs);
+ get_seg(&env->segs[R_GS], &sregs.gs);
+ get_seg(&env->segs[R_SS], &sregs.ss);
+
+ get_seg(&env->tr, &sregs.tr);
+ get_seg(&env->ldt, &sregs.ldt);
+
+ env->idt.limit = sregs.idt.limit;
+ env->idt.base = sregs.idt.base;
+ env->gdt.limit = sregs.gdt.limit;
+ env->gdt.base = sregs.gdt.base;
+
+ env->cr[0] = sregs.cr0;
+ env->cr[2] = sregs.cr2;
+ env->cr[3] = sregs.cr3;
+ env->cr[4] = sregs.cr4;
+
+ cpu_set_apic_base(env, sregs.apic_base);
+
+ env->efer = sregs.efer;
+ //cpu_set_apic_tpr(env, sregs.cr8);
+
+#define HFLAG_COPY_MASK ~( \
+ HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \
+ HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \
+ HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \
+ HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)
+
+
+
+ hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
+ hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT);
+ hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) &
+ (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK);
+ hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK));
+ hflags |= (env->cr[4] & CR4_OSFXSR_MASK) <<
+ (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT);
+
+ if (env->efer & MSR_EFER_LMA) {
+ hflags |= HF_LMA_MASK;
+ }
+
+ if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) {
+ hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+ } else {
+ hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >>
+ (DESC_B_SHIFT - HF_CS32_SHIFT);
+ hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >>
+ (DESC_B_SHIFT - HF_SS32_SHIFT);
+ if (!(env->cr[0] & CR0_PE_MASK) ||
+ (env->eflags & VM_MASK) ||
+ !(hflags & HF_CS32_MASK)) {
+ hflags |= HF_ADDSEG_MASK;
+ } else {
+ hflags |= ((env->segs[R_DS].base |
+ env->segs[R_ES].base |
+ env->segs[R_SS].base) != 0) <<
+ HF_ADDSEG_SHIFT;
+ }
+ }
+ env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags;
+ 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;
+ msrs[n++].index = MSR_VM_HSAVE_PA;
+#ifdef TARGET_X86_64
+ if (lm_capable_kernel) {
+ msrs[n++].index = MSR_CSTAR;
+ msrs[n++].index = MSR_KERNELGSBASE;
+ msrs[n++].index = MSR_FMASK;
+ msrs[n++].index = MSR_LSTAR;
+ }
+#endif
+ rc = kvm_get_msrs(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];
+
+#ifdef __x86_64__
+ asm volatile("cpuid"
+ : "=a"(vec[0]), "=b"(vec[1]),
+ "=c"(vec[2]), "=d"(vec[3])
+ : "0"(function) : "cc");
+#else
+ asm volatile("pusha \n\t"
+ "cpuid \n\t"
+ "mov %%eax, 0(%1) \n\t"
+ "mov %%ebx, 4(%1) \n\t"
+ "mov %%ecx, 8(%1) \n\t"
+ "mov %%edx, 12(%1) \n\t"
+ "popa"
+ : : "a"(function), "S"(vec)
+ : "memory", "cc");
+#endif
+
+ 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;
+
+ host_cpuid(function, &h_eax, NULL, NULL, &h_edx);
+
+ // 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 (!kvm_nested && (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];
+ }
+ // "Hypervisor present" bit for Microsoft guests
+ if (function == 1)
+ e->ecx |= (1u << 31);
+
+ // 3dnow isn't properly emulated yet
+ if (function == 0x80000001)
+ e->edx &= ~0xc0000000;
+}
+
+struct kvm_para_features {
+ int cap;
+ int feature;
+} para_features[] = {
+#ifdef KVM_CAP_CLOCKSOURCE
+ { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE },
+#endif
+#ifdef KVM_CAP_NOP_IO_DELAY
+ { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY },
+#endif
+#ifdef KVM_CAP_PV_MMU
+ { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
+#endif
+#ifdef KVM_CAP_CR3_CACHE
+ { KVM_CAP_CR3_CACHE, KVM_FEATURE_CR3_CACHE },
+#endif
+ { -1, -1 }
+};
+
+static int get_para_features(kvm_context_t kvm_context)
+{
+ int i, features = 0;
+
+ for (i = 0; i < ARRAY_SIZE(para_features)-1; i++) {
+ if (kvm_check_extension(kvm_context, para_features[i].cap))
+ features |= (1 << para_features[i].feature);
+ }
+
+ return features;
+}
+
+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\0\0\0", 12);
+ pv_ent = &cpuid_ent[cpuid_nent++];
+ memset(pv_ent, 0, sizeof(*pv_ent));
+ pv_ent->function = KVM_CPUID_SIGNATURE;
+ pv_ent->eax = 0;
+ pv_ent->ebx = signature[0];
+ pv_ent->ecx = signature[1];
+ pv_ent->edx = signature[2];
+
+ pv_ent = &cpuid_ent[cpuid_nent++];
+ memset(pv_ent, 0, sizeof(*pv_ent));
+ pv_ent->function = KVM_CPUID_FEATURES;
+ pv_ent->eax = get_para_features(kvm_context);
+#endif
+
+ 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->interrupt_request & CPU_INTERRUPT_NMI)) {
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ }
+ return 1;
+}
+
+void kvm_arch_pre_kvm_run(void *opaque, CPUState *env)
+{
+ if (!kvm_irqchip_in_kernel(kvm_context))
+ kvm_set_cr8(kvm_context, env->cpu_index, cpu_get_apic_tpr(env));
+}
+
+void kvm_arch_post_kvm_run(void *opaque, CPUState *env)
+{
+ int vcpu = env->cpu_index;
+
+ cpu_single_env = env;
+
+ env->eflags = kvm_get_interrupt_flag(kvm_context, vcpu)
+ ? env->eflags | IF_MASK : env->eflags & ~IF_MASK;
+
+ 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)) ||
+ (env->interrupt_request & CPU_INTERRUPT_NMI))
+ return 1;
+ return 0;
+}
+
+int kvm_arch_try_push_interrupts(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r, irq;
+
+ if (kvm_is_ready_for_interrupt_injection(kvm_context, env->cpu_index) &&
+ (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;
+}
+
+#ifdef KVM_CAP_USER_NMI
+void kvm_arch_push_nmi(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r;
+
+ if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI)))
+ return;
+
+ env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ r = kvm_inject_nmi(kvm_context, env->cpu_index);
+ if (r < 0)
+ printf("cpu %d fail inject NMI\n", env->cpu_index);
+}
+#endif /* KVM_CAP_USER_NMI */
+
+void kvm_arch_update_regs_for_sipi(CPUState *env)
+{
+ SegmentCache cs = env->segs[R_CS];
+
+ kvm_arch_save_regs(env);
+ env->segs[R_CS] = cs;
+ env->eip = 0;
+ kvm_arch_load_regs(env);
+}
+
+int handle_tpr_access(void *opaque, int vcpu,
+ uint64_t rip, int is_write)
+{
+ kvm_tpr_access_report(cpu_single_env, rip, is_write);
+ return 0;
+}
+
+void kvm_arch_cpu_reset(CPUState *env)
+{
+ kvm_arch_load_regs(env);
+ if (env->cpu_index != 0) {
+ if (kvm_irqchip_in_kernel(kvm_context)) {
+#ifdef KVM_CAP_MP_STATE
+ kvm_reset_mpstate(kvm_context, env->cpu_index);
+#endif
+ } else {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ }
+ }
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ uint8_t int3 = 0xcc;
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, &int3, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ uint8_t int3;
+
+ if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc ||
+ cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1))
+ return -EINVAL;
+ return 0;
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static struct {
+ target_ulong addr;
+ int len;
+ int type;
+} hw_breakpoint[4];
+
+static int nb_hw_breakpoint;
+
+static int find_hw_breakpoint(target_ulong addr, int len, int type)
+{
+ int n;
+
+ for (n = 0; n < nb_hw_breakpoint; n++)
+ if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type &&
+ (hw_breakpoint[n].len == len || len == -1))
+ return n;
+ return -1;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ switch (type) {
+ case GDB_BREAKPOINT_HW:
+ len = 1;
+ break;
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_ACCESS:
+ switch (len) {
+ case 1:
+ break;
+ case 2:
+ case 4:
+ case 8:
+ if (addr & (len - 1))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ if (nb_hw_breakpoint == 4)
+ return -ENOBUFS;
+
+ if (find_hw_breakpoint(addr, len, type) >= 0)
+ return -EEXIST;
+
+ hw_breakpoint[nb_hw_breakpoint].addr = addr;
+ hw_breakpoint[nb_hw_breakpoint].len = len;
+ hw_breakpoint[nb_hw_breakpoint].type = type;
+ nb_hw_breakpoint++;
+
+ return 0;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ int n;
+
+ n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type);
+ if (n < 0)
+ return -ENOENT;
+
+ nb_hw_breakpoint--;
+ hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint];
+
+ return 0;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+ nb_hw_breakpoint = 0;
+}
+
+static CPUWatchpoint hw_watchpoint;
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ int handle = 0;
+ int n;
+
+ if (arch_info->exception == 1) {
+ if (arch_info->dr6 & (1 << 14)) {
+ if (cpu_single_env->singlestep_enabled)
+ handle = 1;
+ } else {
+ for (n = 0; n < 4; n++)
+ if (arch_info->dr6 & (1 << n))
+ switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
+ case 0x0:
+ handle = 1;
+ break;
+ case 0x1:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = &hw_watchpoint;
+ hw_watchpoint.vaddr = hw_breakpoint[n].addr;
+ hw_watchpoint.flags = BP_MEM_WRITE;
+ break;
+ case 0x3:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = &hw_watchpoint;
+ hw_watchpoint.vaddr = hw_breakpoint[n].addr;
+ hw_watchpoint.flags = BP_MEM_ACCESS;
+ break;
+ }
+ }
+ } else if (kvm_find_sw_breakpoint(arch_info->pc))
+ handle = 1;
+
+ if (!handle)
+ kvm_update_guest_debug(cpu_single_env,
+ (arch_info->exception == 1) ?
+ KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP);
+
+ return handle;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+ const uint8_t type_code[] = {
+ [GDB_BREAKPOINT_HW] = 0x0,
+ [GDB_WATCHPOINT_WRITE] = 0x1,
+ [GDB_WATCHPOINT_ACCESS] = 0x3
+ };
+ const uint8_t len_code[] = {
+ [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2
+ };
+ int n;
+
+ if (!TAILQ_EMPTY(&kvm_sw_breakpoints))
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+
+ if (nb_hw_breakpoint > 0) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
+ dbg->arch.debugreg[7] = 0x0600;
+ for (n = 0; n < nb_hw_breakpoint; n++) {
+ dbg->arch.debugreg[n] = hw_breakpoint[n].addr;
+ dbg->arch.debugreg[7] |= (2 << (n * 2)) |
+ (type_code[hw_breakpoint[n].type] << (16 + n*4)) |
+ (len_code[hw_breakpoint[n].len] << (18 + n*4));
+ }
+ }
+}
+#endif
+
+void kvm_arch_do_ioperm(void *_data)
+{
+ struct ioperm_data *data = _data;
+ ioperm(data->start_port, data->num, data->turn_on);
+}
diff --git a/qemu-kvm.c b/qemu-kvm.c
new file mode 100644
index 000000000..01c265bc5
--- /dev/null
+++ b/qemu-kvm.c
@@ -0,0 +1,1359 @@
+/*
+ * qemu/kvm integration
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+#include "config.h"
+#include "config-host.h"
+
+int kvm_allowed = 1;
+int kvm_irqchip = 1;
+int kvm_pit = 1;
+int kvm_nested = 0;
+
+#include <assert.h>
+#include <string.h>
+#include "hw/hw.h"
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "console.h"
+#include "block.h"
+#include "compatfd.h"
+#include "gdbstub.h"
+
+#include "qemu-kvm.h"
+#include <libkvm.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+
+#define false 0
+#define true 1
+
+extern void perror(const char *s);
+
+kvm_context_t kvm_context;
+
+extern int smp_cpus;
+
+pthread_mutex_t qemu_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t qemu_vcpu_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t qemu_system_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t qemu_pause_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t qemu_work_cond = PTHREAD_COND_INITIALIZER;
+__thread struct CPUState *current_env;
+
+static int qemu_system_ready;
+
+#define SIG_IPI (SIGRTMIN+4)
+
+pthread_t io_thread;
+static int io_thread_fd = -1;
+static int io_thread_sigfd = -1;
+
+static CPUState *kvm_debug_cpu_requested;
+
+/* The list of ioperm_data */
+static LIST_HEAD(, ioperm_data) ioperm_head;
+
+static inline unsigned long kvm_get_thread_id(void)
+{
+ return syscall(SYS_gettid);
+}
+
+static void qemu_cond_wait(pthread_cond_t *cond)
+{
+ CPUState *env = cpu_single_env;
+ static const struct timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 100000,
+ };
+
+ pthread_cond_timedwait(cond, &qemu_mutex, &ts);
+ cpu_single_env = env;
+}
+
+static void sig_ipi_handler(int n)
+{
+}
+
+static void on_vcpu(CPUState *env, void (*func)(void *data), void *data)
+{
+ struct qemu_work_item wi;
+
+ if (env == current_env) {
+ func(data);
+ return;
+ }
+
+ wi.func = func;
+ wi.data = data;
+ if (!env->kvm_cpu_state.queued_work_first)
+ env->kvm_cpu_state.queued_work_first = &wi;
+ else
+ env->kvm_cpu_state.queued_work_last->next = &wi;
+ env->kvm_cpu_state.queued_work_last = &wi;
+ wi.next = NULL;
+ wi.done = false;
+
+ pthread_kill(env->kvm_cpu_state.thread, SIG_IPI);
+ while (!wi.done)
+ qemu_cond_wait(&qemu_work_cond);
+}
+
+static void inject_interrupt(void *data)
+{
+ cpu_interrupt(current_env, (int)data);
+}
+
+void kvm_inject_interrupt(CPUState *env, int mask)
+{
+ on_vcpu(env, inject_interrupt, (void *)mask);
+}
+
+void kvm_update_interrupt_request(CPUState *env)
+{
+ int signal = 0;
+
+ if (env) {
+ if (!current_env || !current_env->kvm_cpu_state.created)
+ signal = 1;
+ /*
+ * Testing for created here is really redundant
+ */
+ if (current_env && current_env->kvm_cpu_state.created &&
+ env != current_env && !env->kvm_cpu_state.signalled)
+ signal = 1;
+
+ if (signal) {
+ env->kvm_cpu_state.signalled = 1;
+ if (env->kvm_cpu_state.thread)
+ pthread_kill(env->kvm_cpu_state.thread, SIG_IPI);
+ }
+ }
+}
+
+void kvm_update_after_sipi(CPUState *env)
+{
+ env->kvm_cpu_state.sipi_needed = 1;
+ kvm_update_interrupt_request(env);
+}
+
+void kvm_apic_init(CPUState *env)
+{
+ if (env->cpu_index != 0)
+ env->kvm_cpu_state.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, void *data)
+{
+ CPUState *env = (CPUState *)data;
+
+ pthread_mutex_lock(&qemu_mutex);
+ kvm_arch_post_kvm_run(opaque, env);
+}
+
+static int pre_kvm_run(void *opaque, void *data)
+{
+ CPUState *env = (CPUState *)data;
+
+ kvm_arch_pre_kvm_run(opaque, env);
+
+ if (env->interrupt_request & CPU_INTERRUPT_EXIT)
+ return 1;
+ pthread_mutex_unlock(&qemu_mutex);
+ return 0;
+}
+
+static void kvm_do_load_registers(void *_env)
+{
+ CPUState *env = _env;
+
+ kvm_arch_load_regs(env);
+}
+
+void kvm_load_registers(CPUState *env)
+{
+ if (kvm_enabled() && qemu_system_ready)
+ on_vcpu(env, kvm_do_load_registers, env);
+}
+
+static void kvm_do_save_registers(void *_env)
+{
+ CPUState *env = _env;
+
+ kvm_arch_save_regs(env);
+}
+
+void kvm_save_registers(CPUState *env)
+{
+ if (kvm_enabled())
+ on_vcpu(env, kvm_do_save_registers, env);
+}
+
+int kvm_cpu_exec(CPUState *env)
+{
+ int r;
+
+ r = kvm_run(kvm_context, env->cpu_index, env);
+ 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 || (env && env->kvm_cpu_state.stopped))
+ return 0;
+ if (!env->halted)
+ return 1;
+ return kvm_arch_has_work(env);
+}
+
+static void flush_queued_work(CPUState *env)
+{
+ struct qemu_work_item *wi;
+
+ if (!env->kvm_cpu_state.queued_work_first)
+ return;
+
+ while ((wi = env->kvm_cpu_state.queued_work_first)) {
+ env->kvm_cpu_state.queued_work_first = wi->next;
+ wi->func(wi->data);
+ wi->done = true;
+ }
+ env->kvm_cpu_state.queued_work_last = NULL;
+ pthread_cond_broadcast(&qemu_work_cond);
+}
+
+static void kvm_main_loop_wait(CPUState *env, int timeout)
+{
+ struct timespec ts;
+ int r, e;
+ siginfo_t siginfo;
+ sigset_t waitset;
+
+ pthread_mutex_unlock(&qemu_mutex);
+
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+ sigemptyset(&waitset);
+ sigaddset(&waitset, SIG_IPI);
+
+ r = sigtimedwait(&waitset, &siginfo, &ts);
+ e = errno;
+
+ pthread_mutex_lock(&qemu_mutex);
+
+ if (r == -1 && !(e == EAGAIN || e == EINTR)) {
+ printf("sigtimedwait: %s\n", strerror(e));
+ exit(1);
+ }
+
+ cpu_single_env = env;
+ flush_queued_work(env);
+
+ if (env->kvm_cpu_state.stop) {
+ env->kvm_cpu_state.stop = 0;
+ env->kvm_cpu_state.stopped = 1;
+ pthread_cond_signal(&qemu_pause_cond);
+ }
+
+ env->kvm_cpu_state.signalled = 0;
+}
+
+static int all_threads_paused(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ if (penv->kvm_cpu_state.stop)
+ return 0;
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ return 1;
+}
+
+static void pause_all_threads(void)
+{
+ CPUState *penv = first_cpu;
+
+ assert(!cpu_single_env);
+
+ while (penv) {
+ penv->kvm_cpu_state.stop = 1;
+ pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI);
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ while (!all_threads_paused())
+ qemu_cond_wait(&qemu_pause_cond);
+}
+
+static void resume_all_threads(void)
+{
+ CPUState *penv = first_cpu;
+
+ assert(!cpu_single_env);
+
+ while (penv) {
+ penv->kvm_cpu_state.stop = 0;
+ penv->kvm_cpu_state.stopped = 0;
+ pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI);
+ penv = (CPUState *)penv->next_cpu;
+ }
+}
+
+static void kvm_vm_state_change_handler(void *context, int running)
+{
+ if (running)
+ resume_all_threads();
+ else
+ pause_all_threads();
+}
+
+static void update_regs_for_sipi(CPUState *env)
+{
+ kvm_arch_update_regs_for_sipi(env);
+ env->kvm_cpu_state.sipi_needed = 0;
+}
+
+static void update_regs_for_init(CPUState *env)
+{
+#ifdef TARGET_I386
+ SegmentCache cs = env->segs[R_CS];
+#endif
+
+ cpu_reset(env);
+
+#ifdef TARGET_I386
+ /* restore SIPI vector */
+ if(env->kvm_cpu_state.sipi_needed)
+ env->segs[R_CS] = cs;
+#endif
+
+ env->kvm_cpu_state.init = 0;
+ kvm_arch_load_regs(env);
+}
+
+static void setup_kernel_sigmask(CPUState *env)
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ sigprocmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+
+ kvm_set_signal_mask(kvm_context, env->cpu_index, &set);
+}
+
+void qemu_kvm_system_reset(void)
+{
+ CPUState *penv = first_cpu;
+
+ pause_all_threads();
+
+ qemu_system_reset();
+
+ while (penv) {
+ kvm_arch_cpu_reset(penv);
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ resume_all_threads();
+}
+
+static int kvm_main_loop_cpu(CPUState *env)
+{
+ setup_kernel_sigmask(env);
+
+ pthread_mutex_lock(&qemu_mutex);
+ if (kvm_irqchip_in_kernel(kvm_context))
+ env->halted = 0;
+
+ kvm_qemu_init_env(env);
+#ifdef TARGET_I386
+ kvm_tpr_vcpu_start(env);
+#endif
+
+ cpu_single_env = env;
+ kvm_load_registers(env);
+
+ while (1) {
+ while (!has_work(env))
+ kvm_main_loop_wait(env, 1000);
+ if (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI))
+ env->halted = 0;
+ if (!kvm_irqchip_in_kernel(kvm_context)) {
+ if (env->kvm_cpu_state.init)
+ update_regs_for_init(env);
+ if (env->kvm_cpu_state.sipi_needed)
+ update_regs_for_sipi(env);
+ }
+ if (!env->halted && !env->kvm_cpu_state.init)
+ kvm_cpu_exec(env);
+ env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
+ kvm_main_loop_wait(env, 0);
+ }
+ pthread_mutex_unlock(&qemu_mutex);
+ return 0;
+}
+
+static void *ap_main_loop(void *_env)
+{
+ CPUState *env = _env;
+ sigset_t signals;
+ struct ioperm_data *data = NULL;
+
+ current_env = env;
+ env->thread_id = kvm_get_thread_id();
+ sigfillset(&signals);
+ sigprocmask(SIG_BLOCK, &signals, NULL);
+ kvm_create_vcpu(kvm_context, env->cpu_index);
+ kvm_qemu_init_env(env);
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+ /* do ioperm for io ports of assigned devices */
+ LIST_FOREACH(data, &ioperm_head, entries)
+ on_vcpu(env, kvm_arch_do_ioperm, data);
+#endif
+
+ /* signal VCPU creation */
+ pthread_mutex_lock(&qemu_mutex);
+ current_env->kvm_cpu_state.created = 1;
+ pthread_cond_signal(&qemu_vcpu_cond);
+
+ /* and wait for machine initialization */
+ while (!qemu_system_ready)
+ qemu_cond_wait(&qemu_system_cond);
+ pthread_mutex_unlock(&qemu_mutex);
+
+ kvm_main_loop_cpu(env);
+ return NULL;
+}
+
+void kvm_init_vcpu(CPUState *env)
+{
+ int cpu = env->cpu_index;
+ pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env);
+
+ while (env->kvm_cpu_state.created == 0)
+ qemu_cond_wait(&qemu_vcpu_cond);
+}
+
+int kvm_init_ap(void)
+{
+#ifdef TARGET_I386
+ kvm_tpr_opt_setup();
+#endif
+ qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL);
+
+ signal(SIG_IPI, sig_ipi_handler);
+ return 0;
+}
+
+void qemu_kvm_notify_work(void)
+{
+ uint64_t value = 1;
+ char buffer[8];
+ size_t offset = 0;
+
+ if (io_thread_fd == -1)
+ return;
+
+ memcpy(buffer, &value, sizeof(value));
+
+ while (offset < 8) {
+ ssize_t len;
+
+ len = write(io_thread_fd, buffer + offset, 8 - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0)
+ break;
+
+ offset += len;
+ }
+
+ if (offset != 8)
+ fprintf(stderr, "failed to notify io thread\n");
+}
+
+/* If we have signalfd, we mask out the signals we want to handle and then
+ * use signalfd to listen for them. We rely on whatever the current signal
+ * handler is to dispatch the signals when we receive them.
+ */
+
+static void sigfd_handler(void *opaque)
+{
+ int fd = (unsigned long)opaque;
+ struct qemu_signalfd_siginfo info;
+ struct sigaction action;
+ ssize_t len;
+
+ while (1) {
+ do {
+ len = read(fd, &info, sizeof(info));
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN)
+ break;
+
+ if (len != sizeof(info)) {
+ printf("read from sigfd returned %ld: %m\n", len);
+ return;
+ }
+
+ sigaction(info.ssi_signo, NULL, &action);
+ if (action.sa_handler)
+ action.sa_handler(info.ssi_signo);
+
+ }
+}
+
+/* Used to break IO thread out of select */
+static void io_thread_wakeup(void *opaque)
+{
+ int fd = (unsigned long)opaque;
+ char buffer[8];
+ size_t offset = 0;
+
+ while (offset < 8) {
+ ssize_t len;
+
+ len = read(fd, buffer + offset, 8 - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0)
+ break;
+
+ offset += len;
+ }
+}
+
+int kvm_main_loop(void)
+{
+ int fds[2];
+ sigset_t mask;
+ int sigfd;
+
+ io_thread = pthread_self();
+ qemu_system_ready = 1;
+
+ if (qemu_eventfd(fds) == -1) {
+ fprintf(stderr, "failed to create eventfd\n");
+ return -errno;
+ }
+
+ qemu_set_fd_handler2(fds[0], NULL, io_thread_wakeup, NULL,
+ (void *)(unsigned long)fds[0]);
+
+ io_thread_fd = fds[1];
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGIO);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ sigfd = qemu_signalfd(&mask);
+ if (sigfd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
+ return -errno;
+ }
+
+ fcntl(sigfd, F_SETFL, O_NONBLOCK);
+
+ qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
+ (void *)(unsigned long)sigfd);
+
+ pthread_cond_broadcast(&qemu_system_cond);
+
+ io_thread_sigfd = sigfd;
+ cpu_single_env = NULL;
+
+ while (1) {
+ main_loop_wait(1000);
+ if (qemu_shutdown_requested())
+ break;
+ else if (qemu_powerdown_requested())
+ qemu_system_powerdown();
+ else if (qemu_reset_requested())
+ qemu_kvm_system_reset();
+#ifdef CONFIG_GDBSTUB
+ else if (kvm_debug_cpu_requested) {
+ gdb_set_stop_cpu(kvm_debug_cpu_requested);
+ vm_stop(EXCP_DEBUG);
+ kvm_debug_cpu_requested = NULL;
+ }
+#endif
+ }
+
+ pause_all_threads();
+ pthread_mutex_unlock(&qemu_mutex);
+
+ return 0;
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_debug(void *opaque, void *data, struct kvm_debug_exit_arch *arch_info)
+{
+ int handle = kvm_arch_debug(arch_info);
+ struct CPUState *env = data;
+
+ if (handle) {
+ kvm_debug_cpu_requested = env;
+ env->kvm_cpu_state.stopped = 1;
+ }
+ return handle;
+}
+#endif
+
+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_mmio_read(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ cpu_physical_memory_rw(addr, data, len, 0);
+ return 0;
+}
+
+static int kvm_mmio_write(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ cpu_physical_memory_rw(addr, data, len, 1);
+ return 0;
+}
+
+static int 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, void *data)
+{
+ struct CPUState *env = (struct CPUState *)data;
+
+ /* stop the current vcpu from going back to guest mode */
+ env->kvm_cpu_state.stopped = 1;
+
+ qemu_system_reset_request();
+ return 1;
+}
+
+static struct kvm_callbacks qemu_kvm_ops = {
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ .debug = kvm_debug,
+#endif
+ .inb = kvm_inb,
+ .inw = kvm_inw,
+ .inl = kvm_inl,
+ .outb = kvm_outb,
+ .outw = kvm_outw,
+ .outl = kvm_outl,
+ .mmio_read = kvm_mmio_read,
+ .mmio_write = kvm_mmio_write,
+ .halt = kvm_halt,
+ .shutdown = kvm_shutdown,
+ .io_window = kvm_io_window,
+ .try_push_interrupts = try_push_interrupts,
+#ifdef KVM_CAP_USER_NMI
+ .push_nmi = kvm_arch_push_nmi,
+#endif
+ .post_kvm_run = post_kvm_run,
+ .pre_kvm_run = pre_kvm_run,
+#ifdef TARGET_I386
+ .tpr_access = handle_tpr_access,
+#endif
+#ifdef TARGET_PPC
+ .powerpc_dcr_read = handle_powerpc_dcr_read,
+ .powerpc_dcr_write = handle_powerpc_dcr_write,
+#endif
+};
+
+int kvm_qemu_init()
+{
+ /* Try to initialize kvm */
+ kvm_context = kvm_init(&qemu_kvm_ops, cpu_single_env);
+ if (!kvm_context) {
+ return -1;
+ }
+ pthread_mutex_lock(&qemu_mutex);
+
+ return 0;
+}
+
+#ifdef TARGET_I386
+static int destroy_region_works = 0;
+#endif
+
+int kvm_qemu_create_context(void)
+{
+ int r;
+ if (!kvm_irqchip) {
+ kvm_disable_irqchip_creation(kvm_context);
+ }
+ if (!kvm_pit) {
+ kvm_disable_pit_creation(kvm_context);
+ }
+ if (kvm_create(kvm_context, 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();
+#ifdef TARGET_I386
+ destroy_region_works = kvm_destroy_memory_region_works(kvm_context);
+#endif
+ return 0;
+}
+
+void kvm_qemu_destroy(void)
+{
+ kvm_finalize(kvm_context);
+}
+
+#ifdef TARGET_I386
+static int must_use_aliases_source(target_phys_addr_t addr)
+{
+ if (destroy_region_works)
+ return false;
+ if (addr == 0xa0000 || addr == 0xa8000)
+ return true;
+ return false;
+}
+
+static int must_use_aliases_target(target_phys_addr_t addr)
+{
+ if (destroy_region_works)
+ return false;
+ if (addr >= 0xe0000000 && addr < 0x100000000ull)
+ return true;
+ return false;
+}
+
+static struct mapping {
+ target_phys_addr_t phys;
+ ram_addr_t ram;
+ ram_addr_t len;
+} mappings[50];
+static int nr_mappings;
+
+static struct mapping *find_ram_mapping(ram_addr_t ram_addr)
+{
+ struct mapping *p;
+
+ for (p = mappings; p < mappings + nr_mappings; ++p) {
+ if (p->ram <= ram_addr && ram_addr < p->ram + p->len) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static struct mapping *find_mapping(target_phys_addr_t start_addr)
+{
+ struct mapping *p;
+
+ for (p = mappings; p < mappings + nr_mappings; ++p) {
+ if (p->phys <= start_addr && start_addr < p->phys + p->len) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static void drop_mapping(target_phys_addr_t start_addr)
+{
+ struct mapping *p = find_mapping(start_addr);
+
+ if (p)
+ *p = mappings[--nr_mappings];
+}
+#endif
+
+void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr,
+ unsigned long size,
+ unsigned long phys_offset)
+{
+ int r = 0;
+ unsigned long area_flags = phys_offset & ~TARGET_PAGE_MASK;
+#ifdef TARGET_I386
+ struct mapping *p;
+#endif
+
+ phys_offset &= ~IO_MEM_ROM;
+
+ if (area_flags == IO_MEM_UNASSIGNED) {
+#ifdef TARGET_I386
+ if (must_use_aliases_source(start_addr)) {
+ kvm_destroy_memory_alias(kvm_context, start_addr);
+ return;
+ }
+ if (must_use_aliases_target(start_addr))
+ return;
+#endif
+ kvm_unregister_memory_area(kvm_context, start_addr, size);
+ return;
+ }
+
+ r = kvm_is_containing_region(kvm_context, start_addr, size);
+ if (r)
+ return;
+
+ if (area_flags >= TLB_MMIO)
+ return;
+
+#ifdef TARGET_I386
+ if (must_use_aliases_source(start_addr)) {
+ p = find_ram_mapping(phys_offset);
+ if (p) {
+ kvm_create_memory_alias(kvm_context, start_addr, size,
+ p->phys + (phys_offset - p->ram));
+ }
+ return;
+ }
+#endif
+
+ r = kvm_register_phys_mem(kvm_context, start_addr,
+ phys_ram_base + phys_offset,
+ size, 0);
+ if (r < 0) {
+ printf("kvm_cpu_register_physical_memory: failed\n");
+ exit(1);
+ }
+
+#ifdef TARGET_I386
+ drop_mapping(start_addr);
+ p = &mappings[nr_mappings++];
+ p->phys = start_addr;
+ p->ram = phys_offset;
+ p->len = size;
+#endif
+
+ return;
+}
+
+void kvm_cpu_unregister_physical_memory(target_phys_addr_t start_addr,
+ target_phys_addr_t size,
+ unsigned long phys_offset)
+{
+ kvm_unregister_memory_area(kvm_context, start_addr, size);
+}
+
+int kvm_setup_guest_memory(void *area, unsigned long size)
+{
+ int ret = 0;
+
+#ifdef MADV_DONTFORK
+ if (kvm_enabled() && !kvm_has_sync_mmu())
+ ret = madvise(area, size, MADV_DONTFORK);
+#endif
+
+ if (ret)
+ perror ("madvise");
+
+ return ret;
+}
+
+int kvm_qemu_check_extension(int ext)
+{
+ return kvm_check_extension(kvm_context, ext);
+}
+
+int kvm_qemu_init_env(CPUState *cenv)
+{
+ return kvm_arch_qemu_init_env(cenv);
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+struct kvm_sw_breakpoint_head kvm_sw_breakpoints =
+ TAILQ_HEAD_INITIALIZER(kvm_sw_breakpoints);
+
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc)
+{
+ struct kvm_sw_breakpoint *bp;
+
+ TAILQ_FOREACH(bp, &kvm_sw_breakpoints, entry) {
+ if (bp->pc == pc)
+ return bp;
+ }
+ return NULL;
+}
+
+struct kvm_set_guest_debug_data {
+ struct kvm_guest_debug dbg;
+ int err;
+};
+
+void kvm_invoke_set_guest_debug(void *data)
+{
+ struct kvm_set_guest_debug_data *dbg_data = data;
+
+ dbg_data->err = kvm_set_guest_debug(kvm_context, cpu_single_env->cpu_index,
+ &dbg_data->dbg);
+}
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ struct kvm_set_guest_debug_data data;
+
+ data.dbg.control = 0;
+ if (env->singlestep_enabled)
+ data.dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
+
+ kvm_arch_update_guest_debug(env, &data.dbg);
+ data.dbg.control |= reinject_trap;
+
+ on_vcpu(env, kvm_invoke_set_guest_debug, &data);
+ return data.err;
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ CPUState *env;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(addr);
+ if (bp) {
+ bp->use_count++;
+ return 0;
+ }
+
+ bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint));
+ if (!bp)
+ return -ENOMEM;
+
+ bp->pc = addr;
+ bp->use_count = 1;
+ err = kvm_arch_insert_sw_breakpoint(current_env, bp);
+ if (err) {
+ free(bp);
+ return err;
+ }
+
+ TAILQ_INSERT_HEAD(&kvm_sw_breakpoints, bp, entry);
+ } else {
+ err = kvm_arch_insert_hw_breakpoint(addr, len, type);
+ if (err)
+ return err;
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = kvm_update_guest_debug(env, 0);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ CPUState *env;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(addr);
+ if (!bp)
+ return -ENOENT;
+
+ if (bp->use_count > 1) {
+ bp->use_count--;
+ return 0;
+ }
+
+ err = kvm_arch_remove_sw_breakpoint(current_env, bp);
+ if (err)
+ return err;
+
+ TAILQ_REMOVE(&kvm_sw_breakpoints, bp, entry);
+ qemu_free(bp);
+ } else {
+ err = kvm_arch_remove_hw_breakpoint(addr, len, type);
+ if (err)
+ return err;
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = kvm_update_guest_debug(env, 0);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+ struct kvm_sw_breakpoint *bp, *next;
+ CPUState *env;
+
+ TAILQ_FOREACH_SAFE(bp, &kvm_sw_breakpoints, entry, next) {
+ if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) {
+ /* Try harder to find a CPU that currently sees the breakpoint. */
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (kvm_arch_remove_sw_breakpoint(env, bp) == 0)
+ break;
+ }
+ }
+ }
+ kvm_arch_remove_all_hw_breakpoints();
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ kvm_update_guest_debug(env, 0);
+}
+
+#else /* !KVM_CAP_SET_GUEST_DEBUG */
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ return -EINVAL;
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+}
+#endif /* !KVM_CAP_SET_GUEST_DEBUG */
+
+/*
+ * dirty pages logging
+ */
+/* FIXME: use unsigned long pointer instead of unsigned char */
+unsigned char *kvm_dirty_bitmap = NULL;
+int kvm_physical_memory_set_dirty_tracking(int enable)
+{
+ int r = 0;
+
+ if (!kvm_enabled())
+ return 0;
+
+ if (enable) {
+ if (!kvm_dirty_bitmap) {
+ unsigned bitmap_size = BITMAP_SIZE(phys_ram_size);
+ kvm_dirty_bitmap = qemu_malloc(bitmap_size);
+ if (kvm_dirty_bitmap == NULL) {
+ perror("Failed to allocate dirty pages bitmap");
+ r=-1;
+ }
+ else {
+ r = kvm_dirty_pages_log_enable_all(kvm_context);
+ }
+ }
+ }
+ else {
+ if (kvm_dirty_bitmap) {
+ r = kvm_dirty_pages_log_reset(kvm_context);
+ qemu_free(kvm_dirty_bitmap);
+ kvm_dirty_bitmap = NULL;
+ }
+ }
+ return r;
+}
+
+/* get kvm's dirty pages bitmap and update qemu's */
+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 long page_number, addr, addr1;
+ ram_addr_t ram_addr;
+ unsigned int len = ((mem_size/TARGET_PAGE_SIZE) + 7) / 8;
+
+ /*
+ * bitmap-traveling is faster than memory-traveling (for addr...)
+ * especially when most of the memory is not dirty.
+ */
+ for (i=0; i<len; i++) {
+ c = bitmap[i];
+ while (c>0) {
+ j = ffsl(c) - 1;
+ c &= ~(1u<<j);
+ page_number = i * 8 + j;
+ addr1 = page_number * TARGET_PAGE_SIZE;
+ addr = offset + addr1;
+ ram_addr = cpu_get_physical_page_desc(addr);
+ cpu_physical_memory_set_dirty(ram_addr);
+ n++;
+ }
+ }
+ return 0;
+}
+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;
+}
+
+void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size,
+ int log)
+{
+ if (log)
+ kvm_dirty_pages_log_enable_slot(kvm_context, start, size);
+ else {
+#ifdef TARGET_I386
+ if (must_use_aliases_target(start))
+ return;
+#endif
+ kvm_dirty_pages_log_disable_slot(kvm_context, start, size);
+ }
+}
+
+int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap)
+{
+ unsigned int bsize = BITMAP_SIZE(phys_ram_size);
+ unsigned int brsize = BITMAP_SIZE(ram_size);
+ unsigned int extra_pages = (phys_ram_size - ram_size) / TARGET_PAGE_SIZE;
+ unsigned int extra_bytes = (extra_pages +7)/8;
+ unsigned int hole_start = BITMAP_SIZE(0xa0000);
+ unsigned int hole_end = BITMAP_SIZE(0xc0000);
+
+ memset(bitmap, 0xFF, brsize + extra_bytes);
+ memset(bitmap + hole_start, 0, hole_end - hole_start);
+ memset(bitmap + brsize + extra_bytes, 0, bsize - brsize - extra_bytes);
+
+ return 0;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_set_irq(int irq, int level)
+{
+ return kvm_set_irq_level(kvm_context, irq, level);
+}
+
+#endif
+
+int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf)
+{
+ return kvm_get_dirty_pages(kvm_context, phys_addr, buf);
+}
+
+void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr,
+ unsigned long size, int log, int writable)
+{
+ return kvm_create_phys_mem(kvm_context, start_addr, size, log, writable);
+}
+
+void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr,
+ unsigned long size)
+{
+ kvm_destroy_phys_mem(kvm_context, start_addr, size);
+}
+
+void kvm_mutex_unlock(void)
+{
+ assert(!cpu_single_env);
+ pthread_mutex_unlock(&qemu_mutex);
+}
+
+void kvm_mutex_lock(void)
+{
+ pthread_mutex_lock(&qemu_mutex);
+ cpu_single_env = NULL;
+}
+
+int qemu_kvm_register_coalesced_mmio(target_phys_addr_t addr, unsigned int size)
+{
+ return kvm_register_coalesced_mmio(kvm_context, addr, size);
+}
+
+int qemu_kvm_unregister_coalesced_mmio(target_phys_addr_t addr,
+ unsigned int size)
+{
+ return kvm_unregister_coalesced_mmio(kvm_context, addr, size);
+}
+
+int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
+{
+ return kvm_register_coalesced_mmio(kvm_context, start, size);
+}
+
+int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
+{
+ return kvm_unregister_coalesced_mmio(kvm_context, start, size);
+}
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+void kvm_add_ioperm_data(struct ioperm_data *data)
+{
+ LIST_INSERT_HEAD(&ioperm_head, data, entries);
+}
+
+void kvm_ioperm(CPUState *env, void *data)
+{
+ if (kvm_enabled() && qemu_system_ready)
+ on_vcpu(env, kvm_arch_do_ioperm, data);
+}
+
+#endif
+
+void kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr)
+{
+#ifndef TARGET_IA64
+ void *buf;
+
+#ifdef TARGET_I386
+ if (must_use_aliases_source(start_addr))
+ return;
+#endif
+
+ buf = qemu_malloc((end_addr - start_addr) / 8 + 2);
+ kvm_get_dirty_pages_range(kvm_context, start_addr, end_addr - start_addr,
+ buf, NULL, kvm_get_dirty_bitmap_cb);
+ qemu_free(buf);
+#endif
+}
+
+int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len)
+{
+#ifdef TARGET_I386
+ if (must_use_aliases_source(phys_addr))
+ return 0;
+#endif
+ kvm_qemu_log_memory(phys_addr, len, 1);
+ return 0;
+}
+
+int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len)
+{
+#ifdef TARGET_I386
+ if (must_use_aliases_source(phys_addr))
+ return 0;
+#endif
+ kvm_qemu_log_memory(phys_addr, len, 0);
+ return 0;
+}
+
+/* hack: both libkvm and upstream qemu define kvm_has_sync_mmu(), differently */
+#undef kvm_has_sync_mmu
+int qemu_kvm_has_sync_mmu(void)
+{
+ return kvm_has_sync_mmu(kvm_context);
+}
diff --git a/qemu-kvm.h b/qemu-kvm.h
new file mode 100644
index 000000000..bd8a9e89b
--- /dev/null
+++ b/qemu-kvm.h
@@ -0,0 +1,204 @@
+/*
+ * qemu/kvm integration
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+#ifndef THE_ORIGINAL_AND_TRUE_QEMU_KVM_H
+#define THE_ORIGINAL_AND_TRUE_QEMU_KVM_H
+
+#include "cpu.h"
+
+#include <signal.h>
+
+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);
+void kvm_load_mpstate(CPUState *env);
+void kvm_save_mpstate(CPUState *env);
+int kvm_cpu_exec(CPUState *env);
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type);
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type);
+void kvm_remove_all_breakpoints(CPUState *current_env);
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap);
+int kvm_qemu_init_env(CPUState *env);
+int kvm_qemu_check_extension(int ext);
+void kvm_apic_init(CPUState *env);
+int kvm_set_irq(int irq, int level);
+
+int kvm_physical_memory_set_dirty_tracking(int enable);
+int kvm_update_dirty_pages_log(void);
+int kvm_get_phys_ram_page_bitmap(unsigned char *bitmap);
+
+void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *env);
+void qemu_kvm_cpuid_on_env(CPUState *env);
+void kvm_inject_interrupt(CPUState *env, int mask);
+void kvm_update_after_sipi(CPUState *env);
+void kvm_update_interrupt_request(CPUState *env);
+void kvm_cpu_register_physical_memory(target_phys_addr_t start_addr,
+ unsigned long size,
+ unsigned long phys_offset);
+void kvm_cpu_unregister_physical_memory(target_phys_addr_t start_addr,
+ target_phys_addr_t size,
+ unsigned long phys_offset);
+void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr,
+ unsigned long size, int log, int writable);
+
+void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr,
+ unsigned long size);
+void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size,
+ int log);
+int kvm_setup_guest_memory(void *area, unsigned long size);
+int kvm_qemu_create_memory_alias(uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys);
+int kvm_qemu_destroy_memory_alias(uint64_t phys_start);
+
+int kvm_arch_qemu_create_context(void);
+
+void kvm_arch_save_regs(CPUState *env);
+void kvm_arch_load_regs(CPUState *env);
+int kvm_arch_qemu_init_env(CPUState *cenv);
+int kvm_arch_halt(void *opaque, int vcpu);
+void kvm_arch_pre_kvm_run(void *opaque, CPUState *env);
+void kvm_arch_post_kvm_run(void *opaque, CPUState *env);
+int kvm_arch_has_work(CPUState *env);
+int kvm_arch_try_push_interrupts(void *opaque);
+void kvm_arch_push_nmi(void *opaque);
+void kvm_arch_update_regs_for_sipi(CPUState *env);
+void kvm_arch_cpu_reset(CPUState *env);
+
+struct kvm_guest_debug;
+struct kvm_debug_exit_arch;
+
+struct kvm_sw_breakpoint {
+ target_ulong pc;
+ target_ulong saved_insn;
+ int use_count;
+ TAILQ_ENTRY(kvm_sw_breakpoint) entry;
+};
+TAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint);
+
+extern struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info);
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(target_ulong pc);
+int kvm_arch_insert_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_remove_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+void kvm_arch_remove_all_hw_breakpoints(void);
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg);
+
+void qemu_kvm_aio_wait_start(void);
+void qemu_kvm_aio_wait(void);
+void qemu_kvm_aio_wait_end(void);
+
+void qemu_kvm_notify_work(void);
+
+void kvm_tpr_opt_setup();
+void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write);
+int handle_tpr_access(void *opaque, int vcpu,
+ uint64_t rip, int is_write);
+void kvm_tpr_vcpu_start(CPUState *env);
+
+int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf);
+int qemu_kvm_register_coalesced_mmio(target_phys_addr_t addr,
+ unsigned int size);
+int qemu_kvm_unregister_coalesced_mmio(target_phys_addr_t addr,
+ unsigned int size);
+
+void qemu_kvm_system_reset_request(void);
+
+int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size);
+int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size);
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+void kvm_ioperm(CPUState *env, void *data);
+void kvm_arch_do_ioperm(void *_data);
+#endif
+
+#ifdef TARGET_PPC
+int handle_powerpc_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data);
+int handle_powerpc_dcr_write(int vcpu,uint32_t dcrn, uint32_t data);
+#endif
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+#define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8)
+
+#ifdef USE_KVM
+#include "libkvm.h"
+#include "sys-queue.h"
+
+extern int kvm_allowed;
+extern int kvm_nested;
+extern kvm_context_t kvm_context;
+
+struct ioperm_data {
+ unsigned long start_port;
+ unsigned long num;
+ int turn_on;
+ LIST_ENTRY(ioperm_data) entries;
+};
+
+int qemu_kvm_has_sync_mmu(void);
+
+#define kvm_enabled() (kvm_allowed)
+#define qemu_kvm_irqchip_in_kernel() kvm_irqchip_in_kernel(kvm_context)
+#define qemu_kvm_pit_in_kernel() kvm_pit_in_kernel(kvm_context)
+#define kvm_has_sync_mmu() qemu_kvm_has_sync_mmu()
+void kvm_init_vcpu(CPUState *env);
+#else
+#define kvm_enabled() (0)
+#define qemu_kvm_irqchip_in_kernel() (0)
+#define qemu_kvm_pit_in_kernel() (0)
+#define kvm_has_sync_mmu() (0)
+#define kvm_load_registers(env) do {} while(0)
+#define kvm_save_registers(env) do {} while(0)
+static inline void kvm_init_vcpu(CPUState *env) { }
+
+#endif
+
+void kvm_mutex_unlock(void);
+void kvm_mutex_lock(void);
+
+static inline void kvm_sleep_begin(void)
+{
+ if (kvm_enabled())
+ kvm_mutex_unlock();
+}
+
+static inline void kvm_sleep_end(void)
+{
+ if (kvm_enabled())
+ kvm_mutex_lock();
+}
+
+static inline void kvm_set_phys_mem(target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset)
+{
+ kvm_cpu_register_physical_memory(start_addr, size, phys_offset);
+}
+
+
+void kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr);
+
+int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len);
+int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len);
+
+
+static inline int kvm_sync_vcpus(void) { return 0; }
+
+#endif
diff --git a/qemu-lock.h b/qemu-lock.h
index f938e19e1..895e712af 100644
--- a/qemu-lock.h
+++ b/qemu-lock.h
@@ -185,11 +185,11 @@ static inline int testandset (spinlock_t *p)
#elif defined(__ia64)
-#include <ia64intrin.h>
+#include "ia64intrin.h"
static inline int testandset (int *p)
{
- return __sync_lock_test_and_set (p, 1);
+ return (int)cmpxchg_acq(p,0,1);
}
#elif defined(__mips__)
static inline int testandset (int *p)
diff --git a/qemu-tool.c b/qemu-tool.c
index c0b1cad66..30a6ac739 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -26,10 +26,6 @@ struct QEMUBH
void *opaque;
};
-void qemu_service_io(void)
-{
-}
-
void term_printf(const char *fmt, ...)
{
}
diff --git a/savevm.c b/savevm.c
index 729e84977..1b3ba894a 100644
--- a/savevm.c
+++ b/savevm.c
@@ -843,6 +843,7 @@ static int qemu_loadvm_state_v2(QEMUFile *f)
if (ret < 0) {
fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
instance_id, idstr);
+ return ret;
}
}
/* always seek to exact end of record */
diff --git a/sysemu.h b/sysemu.h
index 94cffaf95..fdbefd776 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -43,6 +43,9 @@ void qemu_system_powerdown(void);
#endif
void qemu_system_reset(void);
+void qemu_get_launch_info(int *argc, char ***argv,
+ int *opt_daemonize, const char **opt_incoming);
+
void do_savevm(const char *name);
void do_loadvm(const char *name);
void do_delvm(const char *name);
@@ -97,6 +100,7 @@ extern int graphic_rotate;
extern int no_quit;
extern int semihosting_enabled;
extern int old_param;
+extern int hpagesize;
extern const char *bootp_filename;
extern DisplayState display_state;
@@ -131,6 +135,8 @@ typedef struct DriveInfo {
BlockInterfaceType type;
int bus;
int unit;
+ int used;
+ int drive_opt_idx;
} DriveInfo;
#define MAX_IDE_DEVS 2
@@ -139,10 +145,39 @@ typedef struct DriveInfo {
extern int nb_drives;
extern DriveInfo drives_table[MAX_DRIVES+1];
+extern int extboot_drive;
extern int drive_get_index(BlockInterfaceType type, int bus, int unit);
extern int drive_get_max_bus(BlockInterfaceType type);
+extern void drive_uninit(BlockDriverState *bdrv);
+extern void drive_remove(int index);
+
+struct drive_opt {
+ const char *file;
+ char opt[1024];
+ int used;
+};
+
+extern struct drive_opt drives_opt[MAX_DRIVES];
+extern int nb_drives_opt;
+
+struct QEMUMachine;
+extern int drive_add(const char *file, const char *fmt, ...);
+extern int drive_init(struct drive_opt *arg, int snapshot,
+ struct QEMUMachine *machine);
+
+/* acpi */
+void qemu_system_cpu_hot_add(int cpu, int state);
+void qemu_system_hot_add_init(const char *cpu_model);
+void qemu_system_device_hot_add(int pcibus, int slot, int state);
+
+/* device-hotplug */
+void device_hot_add(int pcibus, const char *type, const char *opts);
+void drive_hot_add(int pcibus, const char *devfn_string, const char *opts);
+void device_hot_remove(int pcibus, int slot);
+void device_hot_remove_success(int pcibus, int slot);
+
/* serial ports */
#define MAX_SERIAL_PORTS 4
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index ce9b3fef8..0f518f872 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -640,6 +640,7 @@ typedef struct CPUX86State {
/* in order to simplify APIC support, we leave this pointer to the
user */
struct APICState *apic_state;
+ int mp_state;
} CPUX86State;
CPUX86State *cpu_x86_init(const char *cpu_model);
diff --git a/target-i386/fake-exec.c b/target-i386/fake-exec.c
new file mode 100644
index 000000000..737286dce
--- /dev/null
+++ b/target-i386/fake-exec.c
@@ -0,0 +1,54 @@
+/*
+ * fake-exec.c
+ *
+ * This is a file for stub functions so that compilation is possible
+ * when TCG CPU emulation is disabled during compilation.
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by & Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * This work is licensed under the GNU GPL licence version 2 or later.
+ *
+ */
+#include "exec.h"
+#include "cpu.h"
+
+int code_copy_enabled = 0;
+
+CCTable cc_table[CC_OP_NB];
+
+void cpu_dump_statistics (CPUState *env, FILE*f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+}
+
+unsigned long code_gen_max_block_size(void)
+{
+ return 32;
+}
+
+void cpu_gen_init(void)
+{
+}
+
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+
+{
+ return 0;
+}
+
+int cpu_x86_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ return 0;
+}
+
+void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
+
+void optimize_flags_init(void)
+{
+}
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 9ff76b8ab..1c544d766 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -30,6 +30,8 @@
#include "qemu-common.h"
#include "kvm.h"
+#include "qemu-kvm.h"
+
//#define DEBUG_MMU
static void add_flagname_to_bitmaps(char *flagname, uint32_t *features,
@@ -89,6 +91,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;
+
typedef struct x86_def_t {
const char *name;
uint32_t level;
@@ -399,6 +403,9 @@ static int cpu_x86_register (CPUX86State *env, const char *cpu_model)
{
const char *model_id = def->model_id;
int c, len, i;
+
+ if (cpu_vendor_string != NULL)
+ model_id = cpu_vendor_string;
if (!model_id)
model_id = "";
len = strlen(model_id);
@@ -1373,7 +1380,7 @@ static void breakpoint_handler(CPUState *env)
static void host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
-#if defined(CONFIG_KVM)
+#if defined(CONFIG_KVM) || defined(USE_KVM)
uint32_t vec[4];
#ifdef __x86_64__
@@ -1538,7 +1545,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index,
/* disable CPU features that KVM cannot support */
/* svm */
- *ecx &= ~4UL;
+ if (!kvm_nested)
+ *ecx &= ~4UL;
/* 3dnow */
*edx &= ~0xc0000000;
}
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 2412ae434..f87bf36e8 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -332,6 +332,7 @@ static int kvm_put_msrs(CPUState *env)
if (kvm_has_msr_star(env))
kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
+ kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave);
#ifdef TARGET_X86_64
/* FIXME if lm capable */
kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
@@ -469,6 +470,7 @@ static int kvm_get_msrs(CPUState *env)
if (kvm_has_msr_star(env))
msrs[n++].index = MSR_STAR;
msrs[n++].index = MSR_IA32_TSC;
+ msrs[n++].index = MSR_VM_HSAVE_PA;
#ifdef TARGET_X86_64
/* FIXME lm_capable_kernel */
msrs[n++].index = MSR_CSTAR;
@@ -512,6 +514,9 @@ static int kvm_get_msrs(CPUState *env)
case MSR_IA32_TSC:
env->tsc = msrs[i].data;
break;
+ case MSR_VM_HSAVE_PA:
+ env->vm_hsave = msrs[i].data;
+ break;
}
}
diff --git a/target-i386/machine.c b/target-i386/machine.c
index faab2eb4c..a7056c77a 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -4,6 +4,7 @@
#include "hw/isa.h"
#include "exec-all.h"
+#include "qemu-kvm.h"
void register_machines(void)
{
@@ -35,6 +36,11 @@ void cpu_save(QEMUFile *f, void *opaque)
int32_t a20_mask;
int i;
+ if (kvm_enabled()) {
+ kvm_save_registers(env);
+ kvm_save_mpstate(env);
+ }
+
for(i = 0; i < CPU_NB_REGS; i++)
qemu_put_betls(f, &env->regs[i]);
qemu_put_betls(f, &env->eip);
@@ -120,7 +126,6 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be64s(f, &env->kernelgsbase);
#endif
qemu_put_be32s(f, &env->smbase);
-
qemu_put_be64s(f, &env->pat);
qemu_put_be32s(f, &env->hflags2);
@@ -134,6 +139,14 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be16s(f, &env->intercept_dr_write);
qemu_put_be32s(f, &env->intercept_exceptions);
qemu_put_8s(f, &env->v_tpr);
+
+ if (kvm_enabled()) {
+ for (i = 0; i < sizeof(env->interrupt_bitmap)/8 ; i++) {
+ qemu_put_be64s(f, &env->interrupt_bitmap[i]);
+ }
+ qemu_put_be64s(f, &env->tsc);
+ qemu_put_be32s(f, &env->mp_state);
+ }
}
#ifdef USE_X86LDOUBLE
@@ -306,5 +319,19 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
/* XXX: compute redundant hflags bits */
env->hflags = hflags;
tlb_flush(env, 1);
+ if (kvm_enabled()) {
+ /* when in-kernel irqchip is used, env->halted causes deadlock
+ because no userspace IRQs will ever clear this flag */
+ env->halted = 0;
+ for (i = 0; i < sizeof(env->interrupt_bitmap)/8; i++) {
+ qemu_get_be64s(f, &env->interrupt_bitmap[i]);
+ }
+ qemu_get_be64s(f, &env->tsc);
+ kvm_load_registers(env);
+ if (version_id >= 5) {
+ qemu_get_be32s(f, &env->mp_state);
+ kvm_load_mpstate(env);
+ }
+ }
return 0;
}
diff --git a/target-ia64/cpu.h b/target-ia64/cpu.h
new file mode 100644
index 000000000..9bad6f668
--- /dev/null
+++ b/target-ia64/cpu.h
@@ -0,0 +1,84 @@
+/*
+ * IA64 virtual CPU header
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Copyright (c) 2007 Intel Corporation
+ * Zhang xiantao <xiantao.zhang@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef CPU_IA64_H
+#define CPU_IA64_H
+#include "config.h"
+#include "ia64intrin.h"
+
+#include<string.h>
+
+#define TARGET_LONG_BITS 64
+
+#define TARGET_PAGE_BITS 16
+
+#define ELF_MACHINE EM_IA_64
+
+#define NB_MMU_MODES 2
+#define CPU_PAL_HALT 1
+#define HF_HALTED_MASK (1 << CPU_PAL_HALT)
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+typedef struct CPUIA64State {
+ CPU_COMMON;
+ uint32_t hflags;
+ int mp_state;
+} 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
+
+extern struct CPUIA64State *env;
+int cpu_get_pic_interrupt(CPUIA64State *s);
+int cpu_exec(CPUState *env1);
+CPUState *cpu_ia64_init(const char * cpu_model);
+
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return 0;
+}
+
+#define CPU_PC_FROM_TB(env, tb) do{}while(0)
+
+#include "cpu-all.h"
+
+/*
+ * These ones really should go to the appropriate tcg header file, if/when
+ * tcg support is added for ia64.
+ */
+void flush_icache_range(unsigned long start, unsigned long stop);
+void tcg_dump_info(FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = 0;
+ *cs_base = 0;
+ *flags = 0;
+}
+
+#endif
diff --git a/target-ia64/exec.h b/target-ia64/exec.h
new file mode 100644
index 000000000..0e6b9e86a
--- /dev/null
+++ b/target-ia64/exec.h
@@ -0,0 +1,57 @@
+/*
+ * IA64 execution defines
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2007 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __IA64_H__
+#define __IA64_H__
+
+//#include "dyngen-exec.h"
+#include "config.h"
+
+#include "dyngen-exec.h"
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#define tcg_qemu_tb_exec(tb_ptr) 0
+
+register struct CPUIA64State *env asm(AREG0);
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+void do_interrupt (CPUState *env);
+
+void cpu_lock(void);
+void cpu_unlock(void);
+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;
+}
+
+#endif
diff --git a/target-ia64/fake-exec.c b/target-ia64/fake-exec.c
new file mode 100644
index 000000000..c11cc3221
--- /dev/null
+++ b/target-ia64/fake-exec.c
@@ -0,0 +1,59 @@
+/*
+ * fake-exec.c for ia64.
+ *
+ * This is a file for stub functions so that compilation is possible
+ * when TCG CPU emulation is disabled during compilation.
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by & Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ *
+ * Copyright 2008 Intel Corporation.
+ * Added by Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This work is licensed under the GNU GPL licence version 2 or later.
+ *
+ */
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+int code_copy_enabled = 0;
+
+void cpu_gen_init(void)
+{
+}
+
+unsigned long code_gen_max_block_size(void)
+{
+ return 32;
+}
+
+int cpu_ia64_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ return 0;
+}
+
+void tcg_dump_info(FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+ return;
+}
+
+void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ while (start < stop) {
+ asm volatile ("fc %0" :: "r"(start));
+ start += 32;
+ }
+ asm volatile (";;sync.i;;srlz.i;;");
+}
+
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+
+{
+ return 0;
+}
diff --git a/target-ia64/firmware.c b/target-ia64/firmware.c
new file mode 100644
index 000000000..bac2721f2
--- /dev/null
+++ b/target-ia64/firmware.c
@@ -0,0 +1,592 @@
+/*
+ * firmware.c : Firmware build logic for ia64 platform.
+ *
+ * Ported from Xen 3.0 Source.
+ * Copyright (c) 2007, Intel Corporation.
+ * Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "cpu.h"
+
+#include "firmware.h"
+
+typedef struct {
+ unsigned long signature;
+ unsigned int type;
+ unsigned int length;
+} HOB_GENERIC_HEADER;
+
+/*
+ * INFO HOB is the first data data in one HOB list
+ * it contains the control information of the HOB list
+ */
+typedef struct {
+ HOB_GENERIC_HEADER header;
+ unsigned long length; // current length of hob
+ unsigned long cur_pos; // current poisiton of hob
+ unsigned long buf_size; // size of hob buffer
+} HOB_INFO;
+
+typedef struct{
+ unsigned long start;
+ unsigned long size;
+} hob_mem_t;
+
+typedef enum {
+ HOB_TYPE_INFO=0,
+ HOB_TYPE_TERMINAL,
+ HOB_TYPE_MEM,
+ HOB_TYPE_PAL_BUS_GET_FEATURES_DATA,
+ HOB_TYPE_PAL_CACHE_SUMMARY,
+ HOB_TYPE_PAL_MEM_ATTRIB,
+ HOB_TYPE_PAL_CACHE_INFO,
+ HOB_TYPE_PAL_CACHE_PROT_INFO,
+ HOB_TYPE_PAL_DEBUG_INFO,
+ HOB_TYPE_PAL_FIXED_ADDR,
+ HOB_TYPE_PAL_FREQ_BASE,
+ HOB_TYPE_PAL_FREQ_RATIOS,
+ HOB_TYPE_PAL_HALT_INFO,
+ HOB_TYPE_PAL_PERF_MON_INFO,
+ HOB_TYPE_PAL_PROC_GET_FEATURES,
+ HOB_TYPE_PAL_PTCE_INFO,
+ HOB_TYPE_PAL_REGISTER_INFO,
+ HOB_TYPE_PAL_RSE_INFO,
+ HOB_TYPE_PAL_TEST_INFO,
+ HOB_TYPE_PAL_VM_SUMMARY,
+ HOB_TYPE_PAL_VM_INFO,
+ HOB_TYPE_PAL_VM_PAGE_SIZE,
+ HOB_TYPE_NR_VCPU,
+ HOB_TYPE_NR_NVRAM,
+ HOB_TYPE_MAX
+} hob_type_t;
+
+static int hob_init(void *buffer ,unsigned long buf_size);
+static int add_pal_hob(void* hob_buf);
+static int add_mem_hob(void* hob_buf, unsigned long dom_mem_size);
+static int add_vcpus_hob(void* hob_buf, unsigned long nr_vcpu);
+static int 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
+add_max_hob_entry(void* hob_buf)
+{
+ long max_hob = 0;
+ return hob_add(hob_buf, HOB_TYPE_MAX, &max_hob, sizeof(long));
+}
+
+static int
+build_hob(void* hob_buf, unsigned long hob_buf_size,
+ unsigned long dom_mem_size, unsigned long vcpus)
+{
+ //Init HOB List
+ if (hob_init(hob_buf, hob_buf_size) < 0) {
+ Hob_Output("buffer too small");
+ goto err_out;
+ }
+
+ if (add_mem_hob(hob_buf,dom_mem_size) < 0) {
+ Hob_Output("Add memory hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_vcpus_hob(hob_buf, vcpus) < 0) {
+ Hob_Output("Add NR_VCPU hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_pal_hob(hob_buf) < 0) {
+ Hob_Output("Add PAL hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_max_hob_entry(hob_buf) < 0) {
+ Hob_Output("Add max hob entry failed, buffer too small");
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ return -1;
+}
+static int
+load_hob(void *hob_buf, unsigned long dom_mem_size, 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;
+}
+
+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;
+
+ kernel_fd = open(filename, O_RDONLY);
+ if (kernel_fd < 0) {
+ Hob_Output("Could not open kernel image\n");
+ goto out_1;
+ }
+
+ if ((kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL) {
+ Hob_Output("Could not allocate decompression state for state file\n");
+ goto out_1;
+ }
+
+ *size = 0;
+
+#define CHUNK 1*1024*1024
+ while(1)
+ {
+ if ((tmp = realloc(image, *size + CHUNK)) == NULL) {
+ Hob_Output("Could not allocate memory for kernel image");
+ free(image);
+ image = NULL;
+ goto out;
+ }
+ image = tmp;
+
+ bytes = gzread(kernel_gfd, image + *size, CHUNK);
+ switch (bytes) {
+ case -1:
+ Hob_Output("Error reading kernel image");
+ free(image);
+ image = NULL;
+ goto out;
+ case 0: /* EOF */
+ goto out;
+ default:
+ *size += bytes;
+ break;
+ }
+ }
+#undef CHUNK
+
+out:
+ if (*size == 0) {
+ Hob_Output("Could not read kernel image");
+ free(image);
+ image = NULL;
+ } else if (image) {
+ /* Shrink allocation to fit image. */
+ tmp = realloc(image, *size);
+ if (tmp)
+ image = tmp;
+ }
+
+ if (kernel_gfd != NULL)
+ gzclose(kernel_gfd);
+ else if (kernel_fd >= 0)
+ close(kernel_fd);
+ return image;
+
+out_1:
+ return NULL;
+}
+
+/*
+ * 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..553a9f9fb
--- /dev/null
+++ b/target-ia64/firmware.h
@@ -0,0 +1,44 @@
+/*
+ * firmwar.h: Firmware build logic head file
+ *
+ * Copyright (c) 2007, Intel Corporation.
+ * Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+#ifndef __FIRM_WARE_H
+#define __FIRM_WARE_
+#include "cpu.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <zlib.h>
+
+#define GFW_SIZE (16UL<<20)
+#define GFW_START ((4UL<<30) - GFW_SIZE)
+
+#define HOB_SIGNATURE 0x3436474953424f48 // "HOBSIG64"
+#define GFW_HOB_START ((4UL<<30) - (14UL<<20)) // 4G - 14M
+#define GFW_HOB_SIZE (1UL<<20) // 1M
+#define HOB_OFFSET (GFW_HOB_START-GFW_START)
+
+#define Hob_Output(s) fprintf(stderr, s)
+
+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/helper.c b/target-ia64/helper.c
new file mode 100644
index 000000000..4a94dcafb
--- /dev/null
+++ b/target-ia64/helper.c
@@ -0,0 +1,5 @@
+
+/*
+ * IA64 emulation helpers for qemu. (Leave it as blank now.)
+ *
+ */
diff --git a/target-ia64/machine.c b/target-ia64/machine.c
new file mode 100644
index 000000000..a32b01a08
--- /dev/null
+++ b/target-ia64/machine.c
@@ -0,0 +1,31 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+#include "exec-all.h"
+#include "qemu-kvm.h"
+
+void register_machines(void)
+{
+ qemu_register_machine(&ipf_machine);
+}
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+
+ if (kvm_enabled()) {
+ kvm_save_registers(env);
+ kvm_save_mpstate(env);
+ }
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+
+ if (kvm_enabled()) {
+ kvm_load_registers(env);
+ kvm_load_mpstate(env);
+ }
+ return 0;
+}
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..78b32f962
--- /dev/null
+++ b/target-ia64/op_helper.c
@@ -0,0 +1,98 @@
+/*
+ * op_helper.c: IA64 emulation cpu micro-operations helpers for qemu.
+ *
+ * Copyright (c) 2007 Intel Corporation
+ * Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#include "qemu-kvm.h"
+#include "qemu-common.h"
+
+CPUState *cpu_ia64_init(const char *cpu_model)
+{
+ CPUState *env;
+ env = qemu_mallocz(sizeof(CPUState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ cpu_reset(env);
+ if (kvm_enabled()) {
+ kvm_qemu_init_env(env);
+ kvm_init_vcpu(env);
+ }
+ return env;
+}
+
+void cpu_reset(CPUIA64State *env)
+{
+}
+
+static inline void set_feature(CPUIA64State *env, int feature)
+{
+}
+
+void cpu_ia64_set_model(CPUIA64State *env, uint32_t id)
+{
+}
+
+void cpu_ia64_close(CPUIA64State *env)
+{
+ free(env);
+}
+
+extern int semihosting_enabled;
+
+void switch_mode(CPUState *env, int mode)
+{
+}
+
+/* Handle a CPU exception. */
+void do_interrupt(CPUIA64State *env)
+{
+ if (kvm_enabled()) {
+ printf("%s: unexpect\n", __FUNCTION__);
+ exit(-1);
+ }
+}
+
+int cpu_ia64_handle_mmu_fault (CPUState *env, target_ulong address,
+ int access_type, int is_user, int is_softmmu)
+{
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return -1;
+}
+
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ return;
+}
+
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ return;
+}
diff --git a/target-ia64/translate.c b/target-ia64/translate.c
new file mode 100644
index 000000000..86f48f50c
--- /dev/null
+++ b/target-ia64/translate.c
@@ -0,0 +1,39 @@
+/*
+ * translation.c : IA64 translation code.
+ * Just put it as blank now, and implement it later.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+static uint16_t *gen_opc_ptr;
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "gen-op.h"
+
+int gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ return 0;
+}
+int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ return 0;
+}
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index f7600c435..221f516c6 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -38,9 +38,10 @@
/* Specific definitions for PowerPC embedded */
/* BookE have 36 bits physical address space */
#define TARGET_PHYS_ADDR_BITS 64
-#if defined(CONFIG_USER_ONLY)
+#if defined(CONFIG_USER_ONLY) || defined(USE_KVM)
/* It looks like a lot of Linux programs assume page size
* is 4kB long. This is evil, but we have to deal with it...
+ * Also kvm for embedded powerpc needs (atm) 4kB aligned pages
*/
#define TARGET_PAGE_BITS 12
#else /* defined(CONFIG_USER_ONLY) */
@@ -1423,4 +1424,11 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
*flags = env->hflags;
}
+/* hidden flags (hflags) - used internally by qemu to represent additional
+ * cpu states.
+ */
+#define HF_HALTED_SHIFT 1
+
+#define HF_HALTED_MASK 1<<HF_HALTED_SHIFT
+
#endif /* !defined (__CPU_PPC_H__) */
diff --git a/target-ppc/fake-exec.c b/target-ppc/fake-exec.c
new file mode 100644
index 000000000..259e06d5a
--- /dev/null
+++ b/target-ppc/fake-exec.c
@@ -0,0 +1,104 @@
+/*
+ * fake-exec.c
+ *
+ * This is a file for stub functions so that compilation is possible
+ * when TCG CPU emulation is disabled during compilation.
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by & Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * This work is licensed under the GNU GPL licence version 2 or later.
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+
+struct ppc_def_t {
+ const unsigned char *name;
+ uint32_t pvr;
+ uint32_t svr;
+ uint64_t insns_flags;
+ uint64_t msr_mask;
+ powerpc_mmu_t mmu_model;
+ powerpc_excp_t excp_model;
+ powerpc_input_t bus_model;
+ uint32_t flags;
+ int bfd_mach;
+ void (*init_proc)(CPUPPCState *env);
+ int (*check_pow)(CPUPPCState *env);
+};
+
+int code_copy_enabled = 0;
+
+void cpu_dump_state (CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+}
+
+void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+}
+
+void cpu_dump_statistics (CPUState *env, FILE*f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+}
+
+unsigned long code_gen_max_block_size(void)
+{
+ return 32;
+}
+
+void cpu_gen_init(void)
+{
+}
+
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+
+{
+ return 0;
+}
+
+int cpu_ppc_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ return 0;
+}
+
+void init_proc_ppc440ep_kvm(CPUPPCState *env)
+{
+ ppc40x_irq_init(env);
+}
+
+static ppc_def_t ppc440ep_kvm = {
+ .name = "440EP KVM",
+ .mmu_model = POWERPC_MMU_SOFT_4xx, /*XXX needed for GDB stub */
+ .init_proc = init_proc_ppc440ep_kvm,
+};
+
+const ppc_def_t *cpu_ppc_find_by_name (const unsigned char *name)
+{
+ return &ppc440ep_kvm;
+}
+
+int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
+{
+ env->mmu_model = def->mmu_model;
+ (*def->init_proc)(env);
+ return 0;
+}
+
+void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
diff --git a/usb-linux.c b/usb-linux.c
index 334b45d30..40e79603b 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -34,6 +34,10 @@
#include "qemu-timer.h"
#include "console.h"
+#if defined(__linux__)
+#define __user
+#endif
+
#include <dirent.h>
#include <sys/ioctl.h>
#include <signal.h>
diff --git a/vl.c b/vl.c
index a1a9cf401..99d1dd9c6 100644
--- a/vl.c
+++ b/vl.c
@@ -39,9 +39,11 @@
#include "cache-utils.h"
#include "block.h"
#include "audio/audio.h"
+#include "hw/device-assignment.h"
#include "migration.h"
#include "kvm.h"
#include "balloon.h"
+#include "qemu-kvm.h"
#include <unistd.h>
#include <fcntl.h>
@@ -149,6 +151,8 @@
#include "exec-all.h"
+#include "qemu-kvm.h"
+
//#define DEBUG_UNUSED_IOPORT
//#define DEBUG_IOPORT
//#define DEBUG_NET
@@ -178,6 +182,7 @@ static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
to store the VM snapshots */
DriveInfo drives_table[MAX_DRIVES+1];
int nb_drives;
+int extboot_drive = -1;
static int vga_ram_size;
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
DisplayState display_state;
@@ -213,6 +218,8 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
int win2k_install_hack = 0;
#endif
int usb_enabled = 0;
+const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE];
+int assigned_devices_index;
int smp_cpus = 1;
const char *vnc_display;
int acpi_enabled = 1;
@@ -223,9 +230,15 @@ int no_shutdown = 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 time_drift_fix = 0;
+unsigned int kvm_shadow_memory = 0;
+const char *mem_path = NULL;
+int hpagesize = 0;
+const char *cpu_vendor_string;
#ifdef TARGET_ARM
int old_param = 0;
#endif
@@ -235,11 +248,8 @@ int alt_grab = 0;
unsigned int nb_prom_envs = 0;
const char *prom_envs[MAX_PROM_ENVS];
#endif
-static int nb_drives_opt;
-static struct drive_opt {
- const char *file;
- char opt[1024];
-} drives_opt[MAX_DRIVES];
+int nb_drives_opt;
+struct drive_opt drives_opt[MAX_DRIVES];
static CPUState *cur_cpu;
static CPUState *next_cpu;
@@ -255,6 +265,16 @@ static QEMUTimer *icount_vm_timer;
uint8_t qemu_uuid[16];
+/* KVM runs the main loop in a separate thread. If we update one of the lists
+ * that are polled before or after select(), we need to make sure to break out
+ * of the select() to ensure the new item is serviced.
+ */
+static void main_loop_break(void)
+{
+ if (kvm_enabled())
+ qemu_kvm_notify_work();
+}
+
/***********************************************************/
/* x86 ISA bus support */
@@ -399,6 +419,8 @@ void isa_unassign_ioport(int start, int length)
ioport_write_table[0][i] = default_ioport_writeb;
ioport_write_table[1][i] = default_ioport_writew;
ioport_write_table[2][i] = default_ioport_writel;
+
+ ioport_opaque[i] = NULL;
}
}
@@ -1329,7 +1351,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) ||
(!use_icount &&
qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
qemu_get_clock(vm_clock))) ||
@@ -2149,22 +2172,56 @@ static int bt_parse(const char *opt)
#define MTD_ALIAS "if=mtd"
#define SD_ALIAS "index=0,if=sd"
-static int drive_add(const char *file, const char *fmt, ...)
+static int drive_opt_get_free_idx(void)
+{
+ int index;
+
+ for (index = 0; index < MAX_DRIVES; index++)
+ if (!drives_opt[index].used) {
+ drives_opt[index].used = 1;
+ return index;
+ }
+
+ return -1;
+}
+
+static int drive_get_free_idx(void)
+{
+ int index;
+
+ for (index = 0; index < MAX_DRIVES; index++)
+ if (!drives_table[index].used) {
+ drives_table[index].used = 1;
+ return index;
+ }
+
+ return -1;
+}
+
+int drive_add(const char *file, const char *fmt, ...)
{
va_list ap;
+ int index = drive_opt_get_free_idx();
- if (nb_drives_opt >= MAX_DRIVES) {
+ if (nb_drives_opt >= MAX_DRIVES || index == -1) {
fprintf(stderr, "qemu: too many drives\n");
- exit(1);
+ return -1;
}
- drives_opt[nb_drives_opt].file = file;
+ drives_opt[index].file = file;
va_start(ap, fmt);
- vsnprintf(drives_opt[nb_drives_opt].opt,
+ vsnprintf(drives_opt[index].opt,
sizeof(drives_opt[0].opt), fmt, ap);
va_end(ap);
- return nb_drives_opt++;
+ nb_drives_opt++;
+ return index;
+}
+
+void drive_remove(int index)
+{
+ drives_opt[index].used = 0;
+ nb_drives_opt--;
}
int drive_get_index(BlockInterfaceType type, int bus, int unit)
@@ -2173,10 +2230,11 @@ int drive_get_index(BlockInterfaceType type, int bus, int unit)
/* seek interface, bus and unit */
- for (index = 0; index < nb_drives; index++)
+ for (index = 0; index < MAX_DRIVES; index++)
if (drives_table[index].type == type &&
drives_table[index].bus == bus &&
- drives_table[index].unit == unit)
+ drives_table[index].unit == unit &&
+ drives_table[index].used)
return index;
return -1;
@@ -2201,8 +2259,22 @@ static void bdrv_format_print(void *opaque, const char *name)
fprintf(stderr, " %s", name);
}
-static int drive_init(struct drive_opt *arg, int snapshot,
- QEMUMachine *machine)
+void drive_uninit(BlockDriverState *bdrv)
+{
+ int i;
+
+ for (i = 0; i < MAX_DRIVES; i++)
+ if (drives_table[i].bdrv == bdrv) {
+ drives_table[i].bdrv = NULL;
+ drives_table[i].used = 0;
+ drive_remove(drives_table[i].drive_opt_idx);
+ nb_drives--;
+ break;
+ }
+}
+
+int drive_init(struct drive_opt *arg, int snapshot,
+ QEMUMachine *machine)
{
char buf[128];
char file[1024];
@@ -2218,11 +2290,12 @@ static int drive_init(struct drive_opt *arg, int snapshot,
int index;
int cache;
int bdrv_flags;
+ int drives_table_idx;
char *str = arg->opt;
static const char * const params[] = { "bus", "unit", "if", "index",
"cyls", "heads", "secs", "trans",
"media", "snapshot", "file",
- "cache", "format", NULL };
+ "cache", "format", "boot", NULL };
if (check_params(buf, sizeof(buf), params, str) < 0) {
fprintf(stderr, "qemu: unknown parameter '%s' in '%s'\n",
@@ -2404,6 +2477,19 @@ static int drive_init(struct drive_opt *arg, int snapshot,
}
}
+ if (get_param_value(buf, sizeof(buf), "boot", str)) {
+ if (!strcmp(buf, "on")) {
+ if (extboot_drive != -1) {
+ fprintf(stderr, "qemu: two bootable drives specified\n");
+ return -1;
+ }
+ extboot_drive = nb_drives;
+ } else if (strcmp(buf, "off")) {
+ fprintf(stderr, "qemu: '%s' invalid boot option\n", str);
+ return -1;
+ }
+ }
+
if (arg->file == NULL)
get_param_value(file, sizeof(file), "file", str);
else
@@ -2455,7 +2541,7 @@ static int drive_init(struct drive_opt *arg, int snapshot,
*/
if (drive_get_index(type, bus_id, unit_id) != -1)
- return 0;
+ return -2;
/* init */
@@ -2468,10 +2554,12 @@ static int drive_init(struct drive_opt *arg, int snapshot,
snprintf(buf, sizeof(buf), "%s%s%i",
devname, mediastr, unit_id);
bdrv = bdrv_new(buf);
- drives_table[nb_drives].bdrv = bdrv;
- drives_table[nb_drives].type = type;
- drives_table[nb_drives].bus = bus_id;
- drives_table[nb_drives].unit = unit_id;
+ drives_table_idx = drive_get_free_idx();
+ drives_table[drives_table_idx].bdrv = bdrv;
+ drives_table[drives_table_idx].type = type;
+ drives_table[drives_table_idx].bus = bus_id;
+ drives_table[drives_table_idx].unit = unit_id;
+ drives_table[drives_table_idx].drive_opt_idx = arg - drives_opt;
nb_drives++;
switch(type) {
@@ -2501,7 +2589,7 @@ static int drive_init(struct drive_opt *arg, int snapshot,
break;
}
if (!file[0])
- return 0;
+ return -2;
bdrv_flags = 0;
if (snapshot) {
bdrv_flags |= BDRV_O_SNAPSHOT;
@@ -2518,7 +2606,7 @@ static int drive_init(struct drive_opt *arg, int snapshot,
file);
return -1;
}
- return 0;
+ return drives_table_idx;
}
/***********************************************************/
@@ -2828,6 +2916,7 @@ int qemu_set_fd_handler2(int fd,
ioh->opaque = opaque;
ioh->deleted = 0;
}
+ main_loop_break();
return 0;
}
@@ -2956,6 +3045,8 @@ 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) {
+ if (kvm_enabled() && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */
+ continue;
ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
if (ret)
return ret;
@@ -3042,6 +3133,8 @@ static int ram_save_block(QEMUFile *f)
int found = 0;
while (addr < phys_ram_size) {
+ if (kvm_enabled() && current_addr == 0)
+ kvm_update_dirty_pages_log(); /* FIXME: propagate errors */
if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) {
uint8_t ch;
@@ -3132,6 +3225,8 @@ static int ram_load_dead(QEMUFile *f, void *opaque)
if (ram_decompress_open(s, f) < 0)
return -EINVAL;
for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+ if (kvm_enabled() && (i>=0xa0000) && (i<0xc0000)) /* do not access video-addresses */
+ continue;
if (ram_decompress_buf(s, buf, 1) < 0) {
fprintf(stderr, "Error while reading ram block header\n");
goto error;
@@ -3195,19 +3290,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
-void qemu_service_io(void)
-{
- CPUState *env = cpu_single_env;
- if (env) {
- cpu_interrupt(env, CPU_INTERRUPT_EXIT);
-#ifdef USE_KQEMU
- if (env->kqemu_enabled) {
- kqemu_cpu_interrupt(env);
- }
-#endif
- }
-}
-
/***********************************************************/
/* bottom halves (can be seen as timers which expire ASAP) */
@@ -3284,6 +3366,7 @@ void qemu_bh_schedule(QEMUBH *bh)
if (env) {
cpu_interrupt(env, CPU_INTERRUPT_EXIT);
}
+ main_loop_break();
}
void qemu_bh_cancel(QEMUBH *bh)
@@ -3321,6 +3404,7 @@ static void qemu_bh_update_timeout(int *timeout)
/* machine registration */
static QEMUMachine *first_machine = NULL;
+QEMUMachine *current_machine = NULL;
int qemu_register_machine(QEMUMachine *m)
{
@@ -3503,6 +3587,7 @@ void qemu_system_reset_request(void)
}
if (cpu_single_env)
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+ main_loop_break();
}
void qemu_system_shutdown_request(void)
@@ -3519,6 +3604,23 @@ void qemu_system_powerdown_request(void)
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
}
+static int qemu_select(int max_fd, fd_set *rfds, fd_set *wfds, fd_set *xfds,
+ struct timeval *tv)
+{
+ int ret;
+
+ /* KVM holds a mutex while QEMU code is running, we need hooks to
+ release the mutex whenever QEMU code sleeps. */
+
+ kvm_sleep_begin();
+
+ ret = select(max_fd, rfds, wfds, xfds, tv);
+
+ kvm_sleep_end();
+
+ return ret;
+}
+
#ifdef _WIN32
static void host_main_loop_wait(int *timeout)
{
@@ -3611,13 +3713,15 @@ void main_loop_wait(int timeout)
slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
}
#endif
- ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
+ ret = qemu_select(nfds + 1, &rfds, &wfds, &xfds, &tv);
if (ret > 0) {
IOHandlerRecord **pioh;
for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) {
ioh->fd_read(ioh->opaque);
+ if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque)))
+ FD_CLR(ioh->fd, &rfds);
}
if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
ioh->fd_write(ioh->opaque);
@@ -3647,7 +3751,8 @@ void main_loop_wait(int timeout)
#endif
/* vm time timers */
- if (vm_running && likely(!(cur_cpu->singlestep_enabled & SSTEP_NOTIMER)))
+ if (vm_running && (!cur_cpu
+ || likely(!(cur_cpu->singlestep_enabled & SSTEP_NOTIMER))))
qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],
qemu_get_clock(vm_clock));
@@ -3669,6 +3774,13 @@ static int main_loop(void)
#endif
CPUState *env;
+
+ if (kvm_enabled()) {
+ kvm_main_loop();
+ cpu_disable_ticks();
+ return 0;
+ }
+
cur_cpu = first_cpu;
next_cpu = cur_cpu->next_cpu ?: first_cpu;
for(;;) {
@@ -3738,6 +3850,8 @@ static int main_loop(void)
if (reset_requested) {
reset_requested = 0;
qemu_system_reset();
+ if (kvm_enabled())
+ kvm_load_registers(env);
ret = EXCP_INTERRUPT;
}
if (powerdown_requested) {
@@ -3745,10 +3859,12 @@ static int main_loop(void)
qemu_system_powerdown();
ret = EXCP_INTERRUPT;
}
+#ifdef CONFIG_GDBSTUB
if (unlikely(ret == EXCP_DEBUG)) {
gdb_set_stop_cpu(cur_cpu);
vm_stop(EXCP_DEBUG);
}
+#endif
/* If all cpus are halted then wait until the next IRQ */
/* XXX: use timeout computed from timers */
if (ret == EXCP_HALTED) {
@@ -3812,7 +3928,8 @@ static int main_loop(void)
static void help(int exitcode)
{
- printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
+ printf("QEMU PC emulator version " QEMU_VERSION " (" KVM_VERSION ")"
+ ", Copyright (c) 2003-2008 Fabrice Bellard\n"
"usage: %s [options] [disk_image]\n"
"\n"
"'disk_image' is a raw hard image image for IDE hard disk 0\n"
@@ -3827,6 +3944,7 @@ static void help(int exitcode)
"-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
" [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n"
" [,cache=writethrough|writeback|none][,format=f]\n"
+ " [,boot=on|off]\n"
" use 'file' as a drive image\n"
"-mtdblock file use 'file' as on-board Flash memory image\n"
"-sd file use 'file' as SecureDigital card image\n"
@@ -3947,6 +4065,20 @@ static void help(int exitcode)
#ifdef CONFIG_KVM
"-enable-kvm enable KVM full virtualization support\n"
#endif
+#ifdef USE_KVM
+#ifndef NO_CPU_EMULATION
+ "-no-kvm disable KVM hardware virtualization\n"
+#endif
+ "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n"
+ "-no-kvm-pit disable KVM kernel mode PIT\n"
+ "-enable-nesting enable support for running a VM inside the VM (AMD only)\n"
+#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__)
+ "-pcidevice host=bus:dev.func[,dma=none][,name=string]\n"
+ " expose a PCI device to the guest OS.\n"
+ " dma=none: don't perform any dma translations (default is to use an iommu)\n"
+ " 'string' is used in log output.\n"
+#endif
+#endif
#ifdef TARGET_I386
"-no-acpi disable ACPI\n"
"-no-hpet disable HPET\n"
@@ -3961,6 +4093,9 @@ 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"
+ "-mem-path set the path to hugetlbfs/tmpfs mounted directory, also enables allocation of guest memory with huge pages\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"
@@ -4055,6 +4190,7 @@ enum {
QEMU_OPTION_no_kqemu,
QEMU_OPTION_kernel_kqemu,
QEMU_OPTION_enable_kvm,
+ QEMU_OPTION_enable_nesting,
QEMU_OPTION_win2k_hack,
QEMU_OPTION_usb,
QEMU_OPTION_usbdevice,
@@ -4063,12 +4199,19 @@ enum {
QEMU_OPTION_no_acpi,
QEMU_OPTION_no_hpet,
QEMU_OPTION_curses,
+ QEMU_OPTION_no_kvm,
+ QEMU_OPTION_no_kvm_irqchip,
+ QEMU_OPTION_no_kvm_pit,
+#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__)
+ QEMU_OPTION_pcidevice,
+#endif
QEMU_OPTION_no_reboot,
QEMU_OPTION_no_shutdown,
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,
@@ -4078,6 +4221,9 @@ enum {
QEMU_OPTION_icount,
QEMU_OPTION_uuid,
QEMU_OPTION_incoming,
+ QEMU_OPTION_tdf,
+ QEMU_OPTION_kvm_shadow_memory,
+ QEMU_OPTION_mempath,
};
typedef struct QEMUOption {
@@ -4146,6 +4292,17 @@ static const QEMUOption qemu_options[] = {
#ifdef CONFIG_KVM
{ "enable-kvm", 0, QEMU_OPTION_enable_kvm },
#endif
+#ifdef USE_KVM
+#ifndef NO_CPU_EMULATION
+ { "no-kvm", 0, QEMU_OPTION_no_kvm },
+#endif
+ { "no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip },
+ { "no-kvm-pit", 0, QEMU_OPTION_no_kvm_pit },
+ { "enable-nesting", 0, QEMU_OPTION_enable_nesting },
+#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__)
+ { "pcidevice", HAS_ARG, QEMU_OPTION_pcidevice },
+#endif
+#endif
#if defined(TARGET_PPC) || defined(TARGET_SPARC)
{ "g", 1, QEMU_OPTION_g },
#endif
@@ -4156,6 +4313,7 @@ static const QEMUOption qemu_options[] = {
{ "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 },
@@ -4184,10 +4342,13 @@ static 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
@@ -4196,6 +4357,7 @@ static const QEMUOption qemu_options[] = {
{ "tb-size", HAS_ARG, QEMU_OPTION_tb_size },
{ "icount", HAS_ARG, QEMU_OPTION_icount },
{ "incoming", HAS_ARG, QEMU_OPTION_incoming },
+ { "mem-path", HAS_ARG, QEMU_OPTION_mempath },
{ NULL },
};
@@ -4433,6 +4595,107 @@ static int qemu_uuid_parse(const char *str, uint8_t *uuid)
#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;
+}
+
+
+static int gethugepagesize(void)
+{
+ int ret, fd;
+ char buf[4096];
+ char *needle = "Hugepagesize:";
+ char *size;
+ unsigned long hugepagesize;
+
+ fd = open("/proc/meminfo", O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ exit(0);
+ }
+
+ ret = read(fd, buf, sizeof(buf));
+ if (ret < 0) {
+ perror("read");
+ exit(0);
+ }
+
+ size = strstr(buf, needle);
+ if (!size)
+ return 0;
+ size += strlen(needle);
+ hugepagesize = strtol(size, NULL, 0);
+ return hugepagesize;
+}
+
+void *alloc_mem_area(size_t memory, unsigned long *len, const char *path)
+{
+ char *filename;
+ void *area;
+ int fd;
+
+ if (asprintf(&filename, "%s/kvm.XXXXXX", path) == -1)
+ return NULL;
+
+ hpagesize = gethugepagesize() * 1024;
+ if (!hpagesize)
+ return NULL;
+
+ fd = mkstemp(filename);
+ if (fd < 0) {
+ perror("mkstemp");
+ free(filename);
+ return NULL;
+ }
+ unlink(filename);
+ free(filename);
+
+ memory = (memory+hpagesize-1) & ~(hpagesize-1);
+
+ /*
+ * ftruncate is not supported by hugetlbfs in older
+ * hosts, so don't bother checking for errors.
+ * If anything goes wrong with it under other filesystems,
+ * mmap will fail.
+ */
+ ftruncate(fd, memory);
+
+ area = mmap(0, memory, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (area == MAP_FAILED) {
+ perror("mmap");
+ close(fd);
+ return NULL;
+ }
+
+ *len = memory;
+ return area;
+}
+
+void *qemu_alloc_physram(unsigned long memory)
+{
+ void *area = NULL;
+ unsigned long map_len = memory;
+
+#ifdef USE_KVM
+ if (mem_path)
+ area = alloc_mem_area(memory, &map_len, mem_path);
+#endif
+ if (!area)
+ area = qemu_vmalloc(memory);
+#ifdef USE_KVM
+ if (kvm_setup_guest_memory(area, map_len))
+ area = NULL;
+#endif
+ return area;
+}
+
#ifndef _WIN32
static void termsig_handler(int signal)
@@ -4554,6 +4817,7 @@ int main(int argc, char **argv, char **envp)
parallel_device_index = 0;
usb_devices_index = 0;
+ assigned_devices_index = 0;
nb_net_clients = 0;
nb_bt_opts = 0;
@@ -4988,6 +5252,36 @@ int main(int argc, char **argv, char **envp)
#endif
break;
#endif
+#ifdef USE_KVM
+ case QEMU_OPTION_no_kvm:
+ kvm_allowed = 0;
+ break;
+ case QEMU_OPTION_no_kvm_irqchip: {
+ extern int kvm_irqchip, kvm_pit;
+ kvm_irqchip = 0;
+ kvm_pit = 0;
+ break;
+ }
+ case QEMU_OPTION_no_kvm_pit: {
+ extern int kvm_pit;
+ kvm_pit = 0;
+ break;
+ }
+ case QEMU_OPTION_enable_nesting: {
+ kvm_nested = 1;
+ break;
+ }
+#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__)
+ case QEMU_OPTION_pcidevice:
+ if (assigned_devices_index >= MAX_DEV_ASSIGN_CMDLINE) {
+ fprintf(stderr, "Too many assigned devices\n");
+ exit(1);
+ }
+ assigned_devices[assigned_devices_index] = optarg;
+ assigned_devices_index++;
+ break;
+#endif
+#endif
case QEMU_OPTION_usb:
usb_enabled = 1;
break;
@@ -5046,6 +5340,15 @@ int main(int argc, char **argv, char **envp)
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_mempath:
+ mem_path = optarg;
+ break;
case QEMU_OPTION_name:
qemu_name = optarg;
break;
@@ -5059,6 +5362,9 @@ int main(int argc, char **argv, char **envp)
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;
@@ -5195,6 +5501,20 @@ int main(int argc, char **argv, char **envp)
}
#endif
+#if USE_KVM
+ if (kvm_enabled()) {
+ if (kvm_qemu_init() < 0) {
+ extern int kvm_allowed;
+ fprintf(stderr, "Could not initialize KVM, will disable KVM support\n");
+#ifdef NO_CPU_EMULATION
+ fprintf(stderr, "Compiled with --disable-cpu-emulation, exiting.\n");
+ exit(1);
+#endif
+ kvm_allowed = 0;
+ }
+ }
+#endif
+
if (pid_file && qemu_create_pidfile(pid_file) != 0) {
if (daemonize) {
uint8_t status = 1;
@@ -5272,7 +5592,7 @@ int main(int argc, char **argv, char **envp)
char buf[1024];
if (net_boot & (1 << i)) {
if (model == NULL)
- model = "ne2k_pci";
+ model = "rtl8139";
snprintf(buf, sizeof(buf), "%s/pxe-%s.bin", bios_dir, model);
if (get_image_size(buf) > 0) {
if (nb_option_roms >= MAX_OPTION_ROMS) {
@@ -5318,7 +5638,21 @@ int main(int argc, char **argv, char **envp)
phys_ram_size += ram_size;
}
- phys_ram_base = qemu_vmalloc(phys_ram_size);
+ /* Initialize kvm */
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+#define KVM_EXTRA_PAGES 3
+#else
+#define KVM_EXTRA_PAGES 0
+#endif
+ if (kvm_enabled()) {
+ 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);
+ }
+ }
+
+ phys_ram_base = qemu_alloc_physram(phys_ram_size);
if (!phys_ram_base) {
fprintf(stderr, "Could not allocate physical memory\n");
exit(1);
@@ -5344,8 +5678,10 @@ int main(int argc, char **argv, char **envp)
if (nb_drives_opt < MAX_DRIVES)
drive_add(NULL, SD_ALIAS);
- /* open the virtual block devices */
-
+ /* open the virtual block devices
+ * note that migration with device
+ * hot add/remove is broken.
+ */
for(i = 0; i < nb_drives_opt; i++)
if (drive_init(&drives_opt[i], snapshot, machine) == -1)
exit(1);
@@ -5442,6 +5778,10 @@ int main(int argc, char **argv, char **envp)
}
}
+ if (kvm_enabled())
+ kvm_init_ap();
+
+#ifdef KVM_UPSTREAM
if (kvm_enabled()) {
int ret;
@@ -5451,10 +5791,13 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
}
+#endif
machine->init(ram_size, vga_ram_size, boot_devices, ds,
kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
+ current_machine = machine;
+
/* Set KVM's vcpu state to qemu's initial CPUState. */
if (kvm_enabled()) {
int ret;