Merge branch 'arm/queue' into 'master'

arm64: Merge arm/queue

See merge request kvm-unit-tests/kvm-unit-tests!23
diff --git a/README.md b/README.md
index 5590975..6e82dc2 100644
--- a/README.md
+++ b/README.md
@@ -190,3 +190,6 @@
 code. git-diff's orderFile feature allows us to specify the order in a
 file.  The orderFile we use is `scripts/git.difforder`; adding the config
 with `git config diff.orderFile scripts/git.difforder` enables it.
+
+We strive to follow the Linux kernels coding style so it's recommended
+to run the kernel's ./scripts/checkpatch.pl on new patches.
diff --git a/arm/Makefile.arm64 b/arm/Makefile.arm64
index e8a38d7..6feac76 100644
--- a/arm/Makefile.arm64
+++ b/arm/Makefile.arm64
@@ -31,6 +31,7 @@
 tests = $(TEST_DIR)/timer.flat
 tests += $(TEST_DIR)/micro-bench.flat
 tests += $(TEST_DIR)/cache.flat
+tests += $(TEST_DIR)/debug.flat
 
 include $(SRCDIR)/$(TEST_DIR)/Makefile.common
 
diff --git a/arm/debug.c b/arm/debug.c
new file mode 100644
index 0000000..e9f8056
--- /dev/null
+++ b/arm/debug.c
@@ -0,0 +1,420 @@
+#include <libcflat.h>
+#include <errata.h>
+#include <asm/setup.h>
+#include <asm/processor.h>
+#include <asm/delay.h>
+#include <asm/smp.h>
+#include <asm/barrier.h>
+#include <asm/io.h>
+
+#define MDSCR_KDE		(1 << 13)
+#define MDSCR_MDE		(1 << 15)
+#define MDSCR_SS		(1 << 0)
+
+#define DBGBCR_LEN8		(0xff << 5)
+#define DBGBCR_EXEC		(0x0 << 3)
+#define DBGBCR_EL1		(0x1 << 1)
+#define DBGBCR_E		(0x1 << 0)
+
+#define DBGWCR_LEN8		(0xff << 5)
+#define DBGWCR_RD		(0x1 << 3)
+#define DBGWCR_WR		(0x2 << 3)
+#define DBGWCR_EL1		(0x1 << 1)
+#define DBGWCR_E		(0x1 << 0)
+
+#define SPSR_D			(1 << 9)
+#define SPSR_SS			(1 << 21)
+
+#define ESR_EC_HW_BP_CURRENT    0x31
+#define ESR_EC_SSTEP_CURRENT    0x33
+#define ESR_EC_WP_CURRENT       0x35
+
+#define ID_AA64DFR0_BRPS_SHIFT	12
+#define ID_AA64DFR0_BRPS_MASK	0xf
+#define ID_AA64DFR0_WRPS_SHIFT	20
+#define ID_AA64DFR0_WRPS_MASK	0xf
+
+static volatile uint64_t hw_bp_idx, hw_bp_addr[16];
+static volatile uint64_t wp_idx, wp_data_addr[16];
+static volatile uint64_t ss_addr[4], ss_idx;
+
+static void hw_bp_handler(struct pt_regs *regs, unsigned int esr)
+{
+	hw_bp_addr[hw_bp_idx++] = regs->pc;
+	regs->pstate |= SPSR_D;
+}
+
+static void wp_handler(struct pt_regs *regs, unsigned int esr)
+{
+	wp_data_addr[wp_idx++] = read_sysreg(far_el1);
+	regs->pstate |= SPSR_D;
+}
+
+static void ss_handler(struct pt_regs *regs, unsigned int esr)
+{
+	ss_addr[ss_idx++] = regs->pc;
+	regs->pstate |= SPSR_SS;
+}
+
+static int get_num_hw_bp(void)
+{
+	uint64_t reg = read_sysreg(id_aa64dfr0_el1);
+	/* Number of breakpoints, minus 1 */
+	uint8_t brps = (reg >> ID_AA64DFR0_BRPS_SHIFT) & ID_AA64DFR0_BRPS_MASK;
+
+	return brps + 1;
+}
+
+static int get_num_wp(void)
+{
+	uint64_t reg = read_sysreg(id_aa64dfr0_el1);
+	/* Number of watchpoints, minus 1 */
+	uint8_t wrps = (reg >> ID_AA64DFR0_WRPS_SHIFT) & ID_AA64DFR0_WRPS_MASK;
+
+	return wrps + 1;
+}
+
+static void write_dbgbcr(int n, uint32_t bcr)
+{
+	switch (n) {
+	case 0:
+		write_sysreg(bcr, dbgbcr0_el1); break;
+	case 1:
+		write_sysreg(bcr, dbgbcr1_el1); break;
+	case 2:
+		write_sysreg(bcr, dbgbcr2_el1); break;
+	case 3:
+		write_sysreg(bcr, dbgbcr3_el1); break;
+	case 4:
+		write_sysreg(bcr, dbgbcr4_el1); break;
+	case 5:
+		write_sysreg(bcr, dbgbcr5_el1); break;
+	case 6:
+		write_sysreg(bcr, dbgbcr6_el1); break;
+	case 7:
+		write_sysreg(bcr, dbgbcr7_el1); break;
+	case 8:
+		write_sysreg(bcr, dbgbcr8_el1); break;
+	case 9:
+		write_sysreg(bcr, dbgbcr9_el1); break;
+	case 10:
+		write_sysreg(bcr, dbgbcr10_el1); break;
+	case 11:
+		write_sysreg(bcr, dbgbcr11_el1); break;
+	case 12:
+		write_sysreg(bcr, dbgbcr12_el1); break;
+	case 13:
+		write_sysreg(bcr, dbgbcr13_el1); break;
+	case 14:
+		write_sysreg(bcr, dbgbcr14_el1); break;
+	case 15:
+		write_sysreg(bcr, dbgbcr15_el1); break;
+	default:
+		report_abort("Invalid bcr");
+	}
+}
+
+static void write_dbgbvr(int n, uint64_t bvr)
+{
+	switch (n) {
+	case 0:
+		write_sysreg(bvr, dbgbvr0_el1); break;
+	case 1:
+		write_sysreg(bvr, dbgbvr1_el1); break;
+	case 2:
+		write_sysreg(bvr, dbgbvr2_el1); break;
+	case 3:
+		write_sysreg(bvr, dbgbvr3_el1); break;
+	case 4:
+		write_sysreg(bvr, dbgbvr4_el1); break;
+	case 5:
+		write_sysreg(bvr, dbgbvr5_el1); break;
+	case 6:
+		write_sysreg(bvr, dbgbvr6_el1); break;
+	case 7:
+		write_sysreg(bvr, dbgbvr7_el1); break;
+	case 8:
+		write_sysreg(bvr, dbgbvr8_el1); break;
+	case 9:
+		write_sysreg(bvr, dbgbvr9_el1); break;
+	case 10:
+		write_sysreg(bvr, dbgbvr10_el1); break;
+	case 11:
+		write_sysreg(bvr, dbgbvr11_el1); break;
+	case 12:
+		write_sysreg(bvr, dbgbvr12_el1); break;
+	case 13:
+		write_sysreg(bvr, dbgbvr13_el1); break;
+	case 14:
+		write_sysreg(bvr, dbgbvr14_el1); break;
+	case 15:
+		write_sysreg(bvr, dbgbvr15_el1); break;
+	default:
+		report_abort("invalid bvr");
+	}
+}
+
+static void write_dbgwcr(int n, uint32_t wcr)
+{
+	switch (n) {
+	case 0:
+		write_sysreg(wcr, dbgwcr0_el1); break;
+	case 1:
+		write_sysreg(wcr, dbgwcr1_el1); break;
+	case 2:
+		write_sysreg(wcr, dbgwcr2_el1); break;
+	case 3:
+		write_sysreg(wcr, dbgwcr3_el1); break;
+	case 4:
+		write_sysreg(wcr, dbgwcr4_el1); break;
+	case 5:
+		write_sysreg(wcr, dbgwcr5_el1); break;
+	case 6:
+		write_sysreg(wcr, dbgwcr6_el1); break;
+	case 7:
+		write_sysreg(wcr, dbgwcr7_el1); break;
+	case 8:
+		write_sysreg(wcr, dbgwcr8_el1); break;
+	case 9:
+		write_sysreg(wcr, dbgwcr9_el1); break;
+	case 10:
+		write_sysreg(wcr, dbgwcr10_el1); break;
+	case 11:
+		write_sysreg(wcr, dbgwcr11_el1); break;
+	case 12:
+		write_sysreg(wcr, dbgwcr12_el1); break;
+	case 13:
+		write_sysreg(wcr, dbgwcr13_el1); break;
+	case 14:
+		write_sysreg(wcr, dbgwcr14_el1); break;
+	case 15:
+		write_sysreg(wcr, dbgwcr15_el1); break;
+	default:
+		report_abort("Invalid wcr");
+	}
+}
+
+static void write_dbgwvr(int n, uint64_t wvr)
+{
+	switch (n) {
+	case 0:
+		write_sysreg(wvr, dbgwvr0_el1); break;
+	case 1:
+		write_sysreg(wvr, dbgwvr1_el1); break;
+	case 2:
+		write_sysreg(wvr, dbgwvr2_el1); break;
+	case 3:
+		write_sysreg(wvr, dbgwvr3_el1); break;
+	case 4:
+		write_sysreg(wvr, dbgwvr4_el1); break;
+	case 5:
+		write_sysreg(wvr, dbgwvr5_el1); break;
+	case 6:
+		write_sysreg(wvr, dbgwvr6_el1); break;
+	case 7:
+		write_sysreg(wvr, dbgwvr7_el1); break;
+	case 8:
+		write_sysreg(wvr, dbgwvr8_el1); break;
+	case 9:
+		write_sysreg(wvr, dbgwvr9_el1); break;
+	case 10:
+		write_sysreg(wvr, dbgwvr10_el1); break;
+	case 11:
+		write_sysreg(wvr, dbgwvr11_el1); break;
+	case 12:
+		write_sysreg(wvr, dbgwvr12_el1); break;
+	case 13:
+		write_sysreg(wvr, dbgwvr13_el1); break;
+	case 14:
+		write_sysreg(wvr, dbgwvr14_el1); break;
+	case 15:
+		write_sysreg(wvr, dbgwvr15_el1); break;
+	default:
+		report_abort("invalid wvr");
+	}
+}
+
+static void reset_debug_state(void)
+{
+	int i, num_bp = get_num_hw_bp();
+	int num_wp = get_num_wp();
+
+	asm volatile("msr daifset, #8");
+
+	write_sysreg(0, osdlr_el1);
+	write_sysreg(0, oslar_el1);
+	isb();
+
+	write_sysreg(0, mdscr_el1);
+	for (i = 0; i < num_bp; i++) {
+		write_dbgbvr(i, 0);
+		write_dbgbcr(i, 0);
+	}
+	for (i = 0; i < num_wp; i++) {
+		write_dbgwvr(i, 0);
+		write_dbgwcr(i, 0);
+	}
+	isb();
+}
+
+static void do_migrate(void)
+{
+	puts("Now migrate the VM, then press a key to continue...\n");
+	(void)getchar();
+	report_info("Migration complete");
+}
+
+static noinline void test_hw_bp(bool migrate)
+{
+	extern unsigned char hw_bp0;
+	uint32_t bcr;
+	uint32_t mdscr;
+	uint64_t addr;
+	int num_bp = get_num_hw_bp();
+	int i;
+
+	install_exception_handler(EL1H_SYNC, ESR_EC_HW_BP_CURRENT, hw_bp_handler);
+
+	reset_debug_state();
+
+	bcr = DBGBCR_LEN8 | DBGBCR_EXEC | DBGBCR_EL1 | DBGBCR_E;
+	for (i = 0, addr = (uint64_t)&hw_bp0; i < num_bp; i++, addr += 4) {
+		write_dbgbcr(i, bcr);
+		write_dbgbvr(i, addr);
+	}
+	isb();
+
+	asm volatile("msr daifclr, #8");
+
+	mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_MDE;
+	write_sysreg(mdscr, mdscr_el1);
+	isb();
+
+	if (migrate) {
+		do_migrate();
+		report(num_bp == get_num_hw_bp(), "brps match after migrate");
+	}
+
+	hw_bp_idx = 0;
+
+	/* Trap on up to 16 debug exception unmask instructions. */
+	asm volatile("hw_bp0:\n"
+	     "msr daifclr, #8; msr daifclr, #8; msr daifclr, #8; msr daifclr, #8\n"
+	     "msr daifclr, #8; msr daifclr, #8; msr daifclr, #8; msr daifclr, #8\n"
+	     "msr daifclr, #8; msr daifclr, #8; msr daifclr, #8; msr daifclr, #8\n"
+	     "msr daifclr, #8; msr daifclr, #8; msr daifclr, #8; msr daifclr, #8\n");
+
+	for (i = 0, addr = (uint64_t)&hw_bp0; i < num_bp; i++, addr += 4)
+		report(hw_bp_addr[i] == addr, "hw breakpoint: %d", i);
+}
+
+static volatile char write_data[16];
+
+static noinline void test_wp(bool migrate)
+{
+	uint32_t wcr;
+	uint32_t mdscr;
+	int num_wp = get_num_wp();
+	int i;
+
+	install_exception_handler(EL1H_SYNC, ESR_EC_WP_CURRENT, wp_handler);
+
+	reset_debug_state();
+
+	wcr = DBGWCR_LEN8 | DBGWCR_RD | DBGWCR_WR | DBGWCR_EL1 | DBGWCR_E;
+	for (i = 0; i < num_wp; i++) {
+		write_dbgwcr(i, wcr);
+		write_dbgwvr(i, (uint64_t)&write_data[i]);
+	}
+	isb();
+
+	asm volatile("msr daifclr, #8");
+
+	mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_MDE;
+	write_sysreg(mdscr, mdscr_el1);
+	isb();
+
+	if (migrate) {
+		do_migrate();
+		report(num_wp == get_num_wp(), "wrps match after migrate");
+	}
+
+	wp_idx = 0;
+
+	for (i = 0; i < num_wp; i++) {
+		write_data[i] = i;
+		asm volatile("msr daifclr, #8");
+	}
+
+	for (i = 0; i < num_wp; i++) {
+		report(wp_data_addr[i] == (uint64_t)&write_data[i],
+			"watchpoint received: %d", i);
+		report(write_data[i] == i, "watchpoint data: %d", i);
+	}
+}
+
+static noinline void test_ss(bool migrate)
+{
+	extern unsigned char ss_start;
+	uint32_t mdscr;
+
+	install_exception_handler(EL1H_SYNC, ESR_EC_SSTEP_CURRENT, ss_handler);
+
+	reset_debug_state();
+
+	ss_idx = 0;
+
+	mdscr = read_sysreg(mdscr_el1) | MDSCR_KDE | MDSCR_SS;
+	write_sysreg(mdscr, mdscr_el1);
+	isb();
+
+	if (migrate) {
+		do_migrate();
+	}
+
+	asm volatile("msr daifclr, #8");
+
+	asm volatile("ss_start:\n"
+			"mrs x0, esr_el1\n"
+			"add x0, x0, #1\n"
+			"msr daifset, #8\n"
+			: : : "x0");
+
+	report(ss_addr[0] == (uint64_t)&ss_start, "single step");
+}
+
+int main(int argc, char **argv)
+{
+	if (argc < 2)
+		report_abort("no test specified");
+
+	if (strcmp(argv[1], "bp") == 0) {
+		report_prefix_push(argv[1]);
+		test_hw_bp(false);
+		report_prefix_pop();
+	} else if (strcmp(argv[1], "bp-migration") == 0) {
+		report_prefix_push(argv[1]);
+		test_hw_bp(true);
+		report_prefix_pop();
+	} else if (strcmp(argv[1], "wp") == 0) {
+		report_prefix_push(argv[1]);
+		test_wp(false);
+		report_prefix_pop();
+	} else if (strcmp(argv[1], "wp-migration") == 0) {
+		report_prefix_push(argv[1]);
+		test_wp(true);
+		report_prefix_pop();
+	} else if (strcmp(argv[1], "ss") == 0) {
+		report_prefix_push(argv[1]);
+		test_ss(false);
+		report_prefix_pop();
+	} else if (strcmp(argv[1], "ss-migration") == 0) {
+		report_prefix_push(argv[1]);
+		test_ss(true);
+		report_prefix_pop();
+	} else {
+		report_abort("Unknown subtest '%s'", argv[1]);
+	}
+
+	return report_summary();
+}
diff --git a/arm/flat.lds b/arm/flat.lds
index 6fb459e..47fcb64 100644
--- a/arm/flat.lds
+++ b/arm/flat.lds
@@ -62,7 +62,6 @@
     /DISCARD/ : {
         *(.note*)
         *(.interp)
-        *(.debug*)
         *(.comment)
         *(.dynamic)
     }
diff --git a/arm/gic.c b/arm/gic.c
index 98135ef..1e3ea80 100644
--- a/arm/gic.c
+++ b/arm/gic.c
@@ -732,21 +732,17 @@
 			"dev2/eventid=20 does not trigger any LPI");
 
 	/*
-	 * re-enable the LPI but willingly do not call invall
-	 * so the change in config is not taken into account.
-	 * The LPI should not hit
+	 * re-enable the LPI. While "A change to the LPI configuration
+	 * is not guaranteed to be visible until an appropriate
+	 * invalidation operation has completed" hardware that doesn't
+	 * implement caches may have delivered the event at any point
+	 * after the enabling. Check the LPI has hit by the time the
+	 * invall is done.
 	 */
 	gicv3_lpi_set_config(8195, LPI_PROP_DEFAULT);
 	stats_reset();
 	cpumask_clear(&mask);
 	its_send_int(dev2, 20);
-	wait_for_interrupts(&mask);
-	report(check_acked(&mask, -1, -1),
-			"dev2/eventid=20 still does not trigger any LPI");
-
-	/* Now call the invall and check the LPI hits */
-	stats_reset();
-	cpumask_clear(&mask);
 	cpumask_set_cpu(3, &mask);
 	its_send_invall(col3);
 	wait_for_interrupts(&mask);
diff --git a/arm/run b/arm/run
index a94e1c7..2153bd3 100755
--- a/arm/run
+++ b/arm/run
@@ -58,8 +58,8 @@
 	pci_testdev="-device pci-testdev"
 fi
 
-M+=",accel=$ACCEL"
-command="$qemu -nodefaults $M -cpu $processor $chr_testdev $pci_testdev"
+A="-accel $ACCEL"
+command="$qemu -nodefaults $M $A -cpu $processor $chr_testdev $pci_testdev"
 command+=" -display none -serial stdio -kernel"
 command="$(migration_cmd) $(timeout_cmd) $command"
 
diff --git a/arm/timer.c b/arm/timer.c
index 27e2e61..c4e7b10 100644
--- a/arm/timer.c
+++ b/arm/timer.c
@@ -226,16 +226,23 @@
 	return difference < time_10ms;
 }
 
-static void test_timer(struct timer_info *info)
+static void disable_timer(struct timer_info *info)
+{
+	info->write_ctl(0);
+	info->irq_received = false;
+}
+
+static void test_timer_pending(struct timer_info *info)
 {
 	u64 now = info->read_counter();
 	u64 time_10s = read_sysreg(cntfrq_el0) * 10;
 	u64 later = now + time_10s;
-	s32 left;
 
-	/* We don't want the irq handler to fire because that will change the
+	/*
+	 * We don't want the irq handler to fire because that will change the
 	 * timer state and we want to test the timer output signal.  We can
-	 * still read the pending state even if it's disabled. */
+	 * still read the pending state even if it's disabled.
+	 */
 	set_timer_irq_enabled(info, false);
 
 	/* Enable the timer, but schedule it for much later */
@@ -248,10 +255,9 @@
 	report(timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_PENDING),
 			"interrupt signal pending");
 
-	/* Disable the timer again and prepare to take interrupts */
-	info->write_ctl(0);
-	info->irq_received = false;
+	disable_timer(info);
 	set_timer_irq_enabled(info, true);
+
 	report(!info->irq_received, "no interrupt when timer is disabled");
 	report(!timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_INACTIVE),
 			"interrupt signal no longer pending");
@@ -261,24 +267,67 @@
 	report(timer_pending(info) && gic_timer_check_state(info, GIC_IRQ_STATE_INACTIVE),
 			"interrupt signal not pending");
 
+	disable_timer(info);
+}
+
+static void test_timer_cval(struct timer_info *info)
+{
 	report(test_cval_10msec(info), "latency within 10 ms");
 	report(info->irq_received, "interrupt received");
 
-	/* Disable the timer again */
-	info->write_ctl(0);
+	disable_timer(info);
+}
 
-	/* Test TVAL and IRQ trigger */
-	info->irq_received = false;
-	info->write_tval(read_sysreg(cntfrq_el0) / 100);	/* 10 ms */
+static void timer_do_wfi(struct timer_info *info)
+{
 	local_irq_disable();
-	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
+	if (info->irq_received)
+		goto out;
 	report_info("waiting for interrupt...");
 	wfi();
+out:
 	local_irq_enable();
-	left = info->read_tval();
+}
+
+static void test_timer_tval(struct timer_info *info)
+{
+	u64 time_10ms = read_sysreg(cntfrq_el0) / 100;
+	s32 left;
+	int i;
+
+	info->write_tval(time_10ms);
+	info->write_ctl(ARCH_TIMER_CTL_ENABLE);
+
+	for (;;) {
+		timer_do_wfi(info);
+		left = info->read_tval();
+		if (info->irq_received || left <= 0)
+			break;
+	}
+
+	/* Wait one second for the GIC to update the interrupt state. */
+	if (left <= 0 && !info->irq_received) {
+		for (i = 0; i < 10; i++) {
+			timer_do_wfi(info);
+			if (info->irq_received)
+				break;
+			mdelay(100);
+		}
+		left = info->read_tval();
+	}
+
 	report(info->irq_received, "interrupt received after TVAL/WFI");
-	report(left < 0, "timer has expired");
+	report(left <= 0, "timer has expired");
 	report_info("TVAL is %d ticks", left);
+
+	disable_timer(info);
+}
+
+static void test_timer(struct timer_info *info)
+{
+	test_timer_cval(info);
+	test_timer_pending(info);
+	test_timer_tval(info);
 }
 
 static void test_vtimer(void)
@@ -329,6 +378,8 @@
 	}
 
 	install_irq_handler(EL1H_IRQ, irq_handler);
+	set_timer_irq_enabled(&ptimer_info, true);
+	set_timer_irq_enabled(&vtimer_info, true);
 	local_irq_enable();
 }
 
diff --git a/arm/unittests.cfg b/arm/unittests.cfg
index 945c2d0..5e67b55 100644
--- a/arm/unittests.cfg
+++ b/arm/unittests.cfg
@@ -194,7 +194,6 @@
 [its-migration]
 file = gic.flat
 smp = $MAX_SMP
-accel = kvm
 extra_params = -machine gic-version=3 -append 'its-migration'
 groups = its migration
 arch = arm64
@@ -202,7 +201,6 @@
 [its-pending-migration]
 file = gic.flat
 smp = $MAX_SMP
-accel = kvm
 extra_params = -machine gic-version=3 -append 'its-pending-migration'
 groups = its migration
 arch = arm64
@@ -210,7 +208,6 @@
 [its-migrate-unmapped-collection]
 file = gic.flat
 smp = $MAX_SMP
-accel = kvm
 extra_params = -machine gic-version=3 -append 'its-migrate-unmapped-collection'
 groups = its migration
 arch = arm64
@@ -241,3 +238,40 @@
 file = cache.flat
 arch = arm64
 groups = cache
+
+# Debug tests
+[debug-bp]
+file = debug.flat
+arch = arm64
+extra_params = -append 'bp'
+groups = debug
+
+[debug-bp-migration]
+file = debug.flat
+arch = arm64
+extra_params = -append 'bp-migration'
+groups = debug migration
+
+[debug-wp]
+file = debug.flat
+arch = arm64
+extra_params = -append 'wp'
+groups = debug
+
+[debug-wp-migration]
+file = debug.flat
+arch = arm64
+extra_params = -append 'wp-migration'
+groups = debug migration
+
+[debug-sstep]
+file = debug.flat
+arch = arm64
+extra_params = -append 'ss'
+groups = debug
+
+[debug-sstep-migration]
+file = debug.flat
+arch = arm64
+extra_params = -append 'ss-migration'
+groups = debug migration
diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash
index cd92ed9..aae5523 100644
--- a/scripts/arch-run.bash
+++ b/scripts/arch-run.bash
@@ -267,7 +267,9 @@
 
 env_errata ()
 {
-	if [ "$ERRATATXT" ] && [ ! -f "$ERRATATXT" ]; then
+	if [ "$ACCEL" = "tcg" ]; then
+		export "ERRATA_FORCE=y"
+	elif [ "$ERRATATXT" ] && [ ! -f "$ERRATATXT" ]; then
 		echo "$ERRATATXT not found. (ERRATATXT=$ERRATATXT)" >&2
 		return 2
 	elif [ "$ERRATATXT" ]; then