OBJS += kvm.o
OBJS += main.o
OBJS += util.o
+OBJS += bios/c-intfake.o
+OBJS += bios/c-int10.o
CFLAGS += $(CPPFLAGS) -Iinclude -g
$(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
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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)
+
--- /dev/null
+#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_ */
#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))
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 {
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 */
#define BZ_KERNEL_START 0x100000UL
static const char *BZIMAGE_MAGIC = "HdrS";
-static const char fakebios_vector[] = { 0xcf };
#define BZ_DEFAULT_SETUP_SECTS 4
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;
* 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;