]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
bios: Add bios stubs generated from assembly files
authorCyrill Gorcunov <gorcunov@gmail.com>
Thu, 1 Apr 2010 17:23:18 +0000 (21:23 +0400)
committerCyrill Gorcunov <gorcunov@gmail.com>
Thu, 1 Apr 2010 18:34:46 +0000 (22:34 +0400)
We substitute hardcoded fake bios handlers with ones generated
from assembly sources. This way we may extend them if needed.

In particular new int0x10 handler just write output into video
guest memory so the node machine may read the output anytime
it needs (though, to be fair, the kernel uses int0x10 only once
at bootup procedure if there was an attempt to boot directly
from disk/floppy, any other output is done via video port I/O
which is not implemented yet).

Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
tools/kvm/Makefile
tools/kvm/bios/int10.S [new file with mode: 0644]
tools/kvm/bios/intfake.S [new file with mode: 0644]
tools/kvm/include/kvm/assembly.h [new file with mode: 0644]
tools/kvm/include/kvm/bios.h
tools/kvm/include/kvm/interrupt.h
tools/kvm/kvm.c

index d546f778787304080097e6b572f45f817b2e55f1..091b2a32084b0ed71725fd0fb5882e5499a420e6 100644 (file)
@@ -6,6 +6,8 @@ OBJS    += ioport.o
 OBJS   += kvm.o
 OBJS   += main.o
 OBJS   += util.o
+OBJS   += bios/c-intfake.o
+OBJS   += bios/c-int10.o
 
 CFLAGS += $(CPPFLAGS) -Iinclude -g
 
@@ -34,6 +36,30 @@ $(PROGRAM): $(OBJS)
 
 $(OBJS):
 
+#
+# BIOS assembly weirdness
+#
+BIOS=-D__ASSEMBLY__ -m32 -march=i386 -Os -fno-strict-aliasing -fomit-frame-pointer
+bios/c-intfake.o: bios/c-intfake.c
+       $(CC) $(CFLAGS) -c bios/c-intfake.c -o bios/c-intfake.o
+bios/c-intfake.c: bios/intfake.bin
+       python bios/bin2c.py -i bios/intfake.bin -o bios/c-intfake.c -n intfake
+bios/intfake.bin: bios/intfake.S
+       $(CC) $(CFLAGS)  $(BIOS) -c -o bios/intfake.o bios/intfake.S
+       objcopy -O binary -j .text bios/intfake.o bios/intfake.bin
+
+bios/c-int10.o: bios/c-int10.c
+       $(CC) $(CFLAGS) -c bios/c-int10.c -o bios/c-int10.o
+bios/c-int10.c: bios/int10.bin
+       python bios/bin2c.py -i bios/int10.bin -o bios/c-int10.c -n int10
+bios/int10.bin: bios/int10.S
+       $(CC) $(CFLAGS)  $(BIOS) -c -o bios/int10.o bios/int10.S
+       objcopy -O binary -j .text bios/int10.o bios/int10.bin
+
+
 clean:
+       rm -f bios/*.bin
+       rm -f bios/*.o
+       rm -f bios/*.c
        rm -f $(OBJS) $(PROGRAM)
 .PHONY: clean
diff --git a/tools/kvm/bios/int10.S b/tools/kvm/bios/int10.S
new file mode 100644 (file)
index 0000000..b64f717
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * IRQ 0x10 handler
+ */
+
+#include <kvm/bios.h>
+#include <kvm/assembly.h>
+
+/*
+ * NOTES:
+ * ======
+ * 1) The code is supposed to enter with CS somewhere in BDA_START area so prepare real mode
+ *    interrupt table properly (if you screw it up -- there is no way to restore
+ *    anything, the code is NOT PORTABLE by any means, it's BIOS after all)
+ *
+ * 2) We switch to own stack which is BIOS_STACK_SIZE bytes long saving all caller's
+ *    data we clobber. The stack is not that deep so mask interrupts at entry and dont
+ *    use 'push' too much
+ */
+
+       .org 0
+       .code16gcc
+
+.macro stack_bios seg
+       mov %ss, %\seg:(ss_old)
+       mov %sp, %\seg:(sp_old)
+       mov %si, %\seg:(stack_clobber)
+       mov $BIOS_STACK_SEG, %si
+       mov %si, %ss
+       mov $BIOS_STACK_SIZE, %sp
+       mov %\seg:(stack_clobber), %si
+.endm
+
+.macro stack_norm seg
+       mov %\seg:(ss_old), %ss
+       mov %\seg:(sp_old), %sp
+.endm
+
+.macro opcode_switch ocode, label
+       test \label, %ah
+       je \label
+.endm
+
+/*
+ * int 10 - video - write character and advance cursor (tty write)
+ *     ah = 0eh
+ *     al = character
+ *     bh = display page (alpha modes)
+ *     bl = foreground color (graphics modes)
+ *
+ * We ignore bx settings
+ */
+ENTRY(int10)
+       cli
+       opcode_switch $0x0e, putchar
+       jmp out
+
+/*
+ * put char in AL at current cursor and
+ * increment cursor position
+ */
+putchar:
+       stack_bios cs
+
+       push %fs
+       push %bx
+
+       mov $VIDEO_BASE_SEG, %bx
+       mov %bx, %fs
+       mov %cs:(cursor), %bx
+       mov %al, %fs:(%bx)
+       inc %bx
+       test $VIDEO_SIZE, %bx
+       jb putchar_new
+       xor %bx, %bx
+putchar_new:
+       mov %bx, %fs:(cursor)
+
+       pop %bx
+       pop %fs
+
+       stack_norm cs
+out:
+       sti
+       IRET
+ENTRY_END(int10)
+/*
+ * private IRQ data
+ */
+cursor:                .word 0
+
+/*
+ * must be last in this file
+ */
+       __ALIGN
+ss_old:                .word 0
+sp_old:                .word 0
+stack_clobber: .word 0
+
diff --git a/tools/kvm/bios/intfake.S b/tools/kvm/bios/intfake.S
new file mode 100644 (file)
index 0000000..64fb451
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * BIOS fake interrupt stub, it does nothing
+ * and lockless
+ */
+
+#include <kvm/bios.h>
+#include <kvm/assembly.h>
+
+       .org 0
+       .code16gcc
+
+ENTRY(intfake)
+       IRET
+ENTRY_END(intfake)
+
diff --git a/tools/kvm/include/kvm/assembly.h b/tools/kvm/include/kvm/assembly.h
new file mode 100644 (file)
index 0000000..89f0455
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef ASSEMBLY_H_
+#define ASSEMBLY_H_
+
+#define __ALIGN        .p2align 4, 0x90
+#define ENTRY(name)    \
+       __ALIGN;        \
+       .globl name;    \
+       name:
+
+#define ENTRY_END(name)
+
+/*
+ * gas produces size override prefix with which
+ * we are unhappy, lets make it hardcoded for
+ * 16 bit mode
+ */
+#define IRET   .byte 0xcf
+
+#endif /* ASSEMBLY_H_ */
index 31c96a656401c79ed5f268b9a24c1b91c8272077..9d62fd17f984b225afd70ffc6e0544996b875cbb 100644 (file)
@@ -18,7 +18,7 @@
 
 #define BIOS_STACK             0xFFF70
 #define BIOS_STACK_SEG         0xFFF7
-#define BIOS_STACK             128
+#define BIOS_STACK_SIZE                128
 
 #define ALIGN(x, a)            (((x)+((a)-1))&~((a)-1))
 
index 36dfc29dd58ec7ded6debbe019f92d9b688c8f76..92fc091367cd3dba319b5280a7ee53a479245709 100644 (file)
@@ -9,6 +9,8 @@ struct real_intr_desc {
        uint16_t segment;
 } __attribute__((packed));
 
+#define REAL_SEGMENT_SHIFT     4
+#define REAL_SEGMENT(addr)     ((addr) >> REAL_SEGMENT_SHIFT)
 #define REAL_INTR_SIZE         (REAL_INTR_VECTORS * sizeof(struct real_intr_desc))
 
 struct interrupt_table {
@@ -19,4 +21,12 @@ void interrupt_table__copy(struct interrupt_table *self, void *dst, unsigned int
 void interrupt_table__setup(struct interrupt_table *self, struct real_intr_desc *entry);
 void interrupt_table__set(struct interrupt_table *self, struct real_intr_desc *entry, unsigned int num);
 
+/*
+ * BIOS stubs
+ */
+extern unsigned char intfake[];
+extern unsigned int intfake_size;
+extern unsigned char int10[];
+extern unsigned int int10_size;
+
 #endif /* KVM__INTERRUPT_H */
index 9d138b4cda3ca72c67eb4f8a1461472d6c27dec5..72e1c90473c7faa9f5a4424b2d16849352740127 100644 (file)
@@ -199,7 +199,6 @@ static int load_flat_binary(struct kvm *self, int fd)
 #define BZ_KERNEL_START                        0x100000UL
 
 static const char *BZIMAGE_MAGIC       = "HdrS";
-static const char fakebios_vector[]    = { 0xcf };
 
 #define BZ_DEFAULT_SETUP_SECTS         4
 
@@ -208,6 +207,7 @@ static bool load_bzimage(struct kvm *self, int fd, const char *kernel_cmdline)
        struct real_intr_desc intr;
        struct boot_params boot;
        unsigned long setup_sects;
+       unsigned int intr_addr;
        ssize_t setup_size;
        void *p;
        int nr;
@@ -255,17 +255,27 @@ static bool load_bzimage(struct kvm *self, int fd, const char *kernel_cmdline)
         * Setup a *fake* real mode vector table, it has only
         * one real hadler which does just iret
         *
-        * we need a place for 1 byte so lets put
-        * it where the BIOS lives -- BDA area
+        * This is where the BIOS lives -- BDA area
         */
-       p = guest_flat_to_host(self, BDA_START);
-       memcpy(p, fakebios_vector, sizeof(fakebios_vector));
+       intr_addr = BIOS_INTR_NEXT(BDA_START + 0, 16);
+       p = guest_flat_to_host(self, intr_addr);
+       memcpy(p, intfake, intfake_size);
        intr = (struct real_intr_desc) {
-               .segment        = BDA_START >> 4,
+               .segment        = REAL_SEGMENT(intr_addr),
                .offset         = 0,
        };
-       p = guest_flat_to_host(self, 0);
        interrupt_table__setup(&self->interrupt_table, &intr);
+
+       intr_addr = BIOS_INTR_NEXT(BDA_START + intfake_size, 16);
+       p = guest_flat_to_host(self, intr_addr);
+       memcpy(p, int10, int10_size);
+       intr = (struct real_intr_desc) {
+               .segment        = REAL_SEGMENT(intr_addr),
+               .offset         = 0,
+       };
+       interrupt_table__set(&self->interrupt_table, &intr, 0x10);
+
+       p = guest_flat_to_host(self, 0);
        interrupt_table__copy(&self->interrupt_table, p, REAL_INTR_SIZE);
 
        return true;