Patch to detect 8254 timer munging in BIOS calls (needs testing)

Matthew Dillon dillon at apollo.backplane.com
Fri Nov 19 14:02:33 PST 2004


    I would appreciate it if those people who were having time
    problems due to video bios calls could test this patch.

    What I attempt to do is adjust the iomap for VM86 mode to fault
    if the BIOS accesses the 8254, flag it, reenable the map, retry
    the instruction, and then restore the timer and print a warning 
    when the BIOS returns.

    The question is: (a) does this work (detect the access and print the
    warning) and (b) prevent runaway clocks.

						-Matt



Index: i386/vm86.c
===================================================================
RCS file: /cvs/src/sys/i386/i386/vm86.c,v
retrieving revision 1.11
diff -u -r1.11 vm86.c
--- i386/vm86.c	7 Aug 2004 03:42:37 -0000	1.11
+++ i386/vm86.c	19 Nov 2004 21:54:15 -0000
@@ -48,6 +48,9 @@
 #include <machine/specialreg.h>
 #include <machine/sysarch.h>
 #include <machine/clock.h>
+#include <bus/isa/i386/isa.h>
+#include <bus/isa/rtc.h>
+#include <i386/isa/timerreg.h>
 
 extern int i386_extend_pcb	(struct proc *);
 extern int vm86pa;
@@ -56,6 +59,22 @@
 extern int vm86_bioscall(struct vm86frame *);
 extern void vm86_biosret(struct vm86frame *);
 
+#define PGTABLE_SIZE	((1024 + 64) * 1024 / PAGE_SIZE)
+#define INTMAP_SIZE	32
+#define IOMAP_SIZE	ctob(IOPAGES)
+#define TSS_SIZE \
+	(sizeof(struct pcb_ext) - sizeof(struct segment_descriptor) + \
+	 INTMAP_SIZE + IOMAP_SIZE + 1)
+
+struct vm86_layout {
+	pt_entry_t	vml_pgtbl[PGTABLE_SIZE];
+	struct 	pcb vml_pcb;
+	struct	pcb_ext vml_ext;
+	char	vml_intmap[INTMAP_SIZE];
+	char	vml_iomap[IOMAP_SIZE];
+	char	vml_iomap_trailer;
+};
+
 void vm86_prepcall(struct vm86frame);
 
 struct system_map {
@@ -71,12 +90,25 @@
 #define	POPF	0x9d
 #define	INTn	0xcd
 #define	IRET	0xcf
+#define INB	0xe4
+#define INW	0xe5
+#define INBDX	0xec
+#define INWDX	0xed
+#define OUTB	0xe6
+#define OUTW	0xe7
+#define OUTBDX	0xee
+#define OUTWDX	0xef
 #define	CALLm	0xff
 #define OPERAND_SIZE_PREFIX	0x66
 #define ADDRESS_SIZE_PREFIX	0x67
 #define PUSH_MASK	~(PSL_VM | PSL_RF | PSL_I)
 #define POP_MASK	~(PSL_VIP | PSL_VIF | PSL_VM | PSL_RF | PSL_IOPL)
 
+static void vm86_setup_timer_fault(void);
+static void vm86_clear_timer_fault(void);
+
+static int vm86_blew_up_timer;
+
 static __inline caddr_t
 MAKE_ADDR(u_short sel, u_short off)
 {
@@ -151,6 +183,9 @@
 	if (vmf->vmf_eflags & PSL_T)
 		retcode = SIGTRAP;
 
+	/*
+	 * Instruction emulation
+	 */
 	addr = MAKE_ADDR(vmf->vmf_cs, vmf->vmf_ip);
 	i_byte = fubyte(addr);
 	if (i_byte == ADDRESS_SIZE_PREFIX) {
@@ -158,6 +193,26 @@
 		inc_ip++;
 	}
 
+	/*
+	 * I/O emulation (TIMER only, a big hack).  Just reenable the
+	 * IO bits involved, flag it, and retry the instruction.
+	 */
+	switch(i_byte) {
+	case OUTB:
+	case OUTW:
+	case OUTBDX:
+	case OUTWDX:
+		vm86_blew_up_timer = 1;
+		/* fall through */
+	case INB:
+	case INW:
+	case INBDX:
+	case INWDX:
+		vm86_clear_timer_fault();
+		/* retry insn */
+		return(0);
+	}
+
 	if (vm86->vm86_has_vme) {
 		switch (i_byte) {
 		case OPERAND_SIZE_PREFIX:
@@ -341,22 +396,6 @@
 	return (SIGBUS);
 }
 
-#define PGTABLE_SIZE	((1024 + 64) * 1024 / PAGE_SIZE)
-#define INTMAP_SIZE	32
-#define IOMAP_SIZE	ctob(IOPAGES)
-#define TSS_SIZE \
-	(sizeof(struct pcb_ext) - sizeof(struct segment_descriptor) + \
-	 INTMAP_SIZE + IOMAP_SIZE + 1)
-
-struct vm86_layout {
-	pt_entry_t	vml_pgtbl[PGTABLE_SIZE];
-	struct 	pcb vml_pcb;
-	struct	pcb_ext vml_ext;
-	char	vml_intmap[INTMAP_SIZE];
-	char	vml_iomap[IOMAP_SIZE];
-	char	vml_iomap_trailer;
-};
-
 void
 vm86_initialize(void)
 {
@@ -581,16 +620,18 @@
 	crit_enter();
 	ASSERT_MP_LOCK_HELD();
 
+	vm86_setup_timer_fault();
 	vmf->vmf_trapno = intnum;
 	error = vm86_bioscall(vmf);
-#if 0
+
 	/*
-	 * removed.  This causes more problems then it solves, we will
-	 * have to find another way to detect inappropriate 8254 writes
-	 * from the bios
+	 * Yes, this happens, especially with video BIOS calls.  The BIOS
+	 * will sometimes eat timer 2 for lunch, and we need timer 2.
 	 */
-	timer_restore();
-#endif
+	if (vm86_blew_up_timer) {
+		timer_restore();
+		printf("Warning: BIOS messed around with the 8254, resetting it\n");
+	}
 	crit_exit();
 	return(error);
 }
@@ -743,3 +784,32 @@
 	}
 	return (error);
 }
+
+/*
+ * Setup the VM86 I/O map to take faults on the timer
+ */
+static void
+vm86_setup_timer_fault(void)
+{
+	struct vm86_layout *vml = (struct vm86_layout *)vm86paddr;
+
+	vml->vml_iomap[TIMER_MODE >> 3] |= 1 << (TIMER_MODE & 7);
+	vml->vml_iomap[TIMER_CNTR0 >> 3] |= 1 << (TIMER_CNTR0 & 7);
+	vml->vml_iomap[TIMER_CNTR1 >> 3] |= 1 << (TIMER_CNTR1 & 7);
+	vml->vml_iomap[TIMER_CNTR2 >> 3] |= 1 << (TIMER_CNTR2 & 7);
+}
+
+/*
+ * Setup the VM86 I/O map to not fault on the timer
+ */
+static void
+vm86_clear_timer_fault(void)
+{
+	struct vm86_layout *vml = (struct vm86_layout *)vm86paddr;
+
+	vml->vml_iomap[TIMER_MODE >> 3] &= ~(1 << (TIMER_MODE & 7));
+	vml->vml_iomap[TIMER_CNTR0 >> 3] &= ~(1 << (TIMER_CNTR0 & 7));
+	vml->vml_iomap[TIMER_CNTR1 >> 3] &= ~(1 << (TIMER_CNTR1 & 7));
+	vml->vml_iomap[TIMER_CNTR2 >> 3] &= ~(1 << (TIMER_CNTR2 & 7));
+}
+





More information about the Kernel mailing list