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