Merge branch 'riscv/sbi' into 'master'

riscv: Add support for SBI tests

See merge request kvm-unit-tests/kvm-unit-tests!67
diff --git a/lib/cpumask.h b/lib/cpumask.h
index be19192..e1e92aa 100644
--- a/lib/cpumask.h
+++ b/lib/cpumask.h
@@ -6,8 +6,9 @@
  */
 #ifndef _CPUMASK_H_
 #define _CPUMASK_H_
-#include <asm/setup.h>
 #include <bitops.h>
+#include <limits.h>
+#include <asm/setup.h>
 
 #define CPUMASK_NR_LONGS ((NR_CPUS + BITS_PER_LONG - 1) / BITS_PER_LONG)
 
@@ -49,46 +50,34 @@
 
 static inline void cpumask_setall(cpumask_t *mask)
 {
-	int i;
-	for (i = 0; i < nr_cpus; i += BITS_PER_LONG)
-		cpumask_bits(mask)[BIT_WORD(i)] = ~0UL;
-	i -= BITS_PER_LONG;
-	if ((nr_cpus - i) < BITS_PER_LONG)
-		cpumask_bits(mask)[BIT_WORD(i)] = BIT_MASK(nr_cpus - i) - 1;
+	memset(mask, 0xff, sizeof(*mask));
 }
 
 static inline void cpumask_clear(cpumask_t *mask)
 {
-	int i;
-	for (i = 0; i < nr_cpus; i += BITS_PER_LONG)
-		cpumask_bits(mask)[BIT_WORD(i)] = 0UL;
+	memset(mask, 0, sizeof(*mask));
 }
 
 static inline bool cpumask_empty(const cpumask_t *mask)
 {
-	int i;
-	for (i = 0; i < nr_cpus; i += BITS_PER_LONG) {
-		if (i < NR_CPUS) { /* silence crazy compiler warning */
-			if (cpumask_bits(mask)[BIT_WORD(i)] != 0UL)
-				return false;
-		}
-	}
-	return true;
+	unsigned long lastmask = BIT_MASK(nr_cpus) - 1;
+
+	for (int i = 0; i < BIT_WORD(nr_cpus); ++i)
+		if (cpumask_bits(mask)[i])
+			return false;
+
+	return !lastmask || !(cpumask_bits(mask)[BIT_WORD(nr_cpus)] & lastmask);
 }
 
 static inline bool cpumask_full(const cpumask_t *mask)
 {
-	int i;
-	for (i = 0; i < nr_cpus; i += BITS_PER_LONG) {
-		if (cpumask_bits(mask)[BIT_WORD(i)] != ~0UL) {
-			if ((nr_cpus - i) >= BITS_PER_LONG)
-				return false;
-			if (cpumask_bits(mask)[BIT_WORD(i)]
-					!= BIT_MASK(nr_cpus - i) - 1)
-				return false;
-		}
-	}
-	return true;
+	unsigned long lastmask = BIT_MASK(nr_cpus) - 1;
+
+	for (int i = 0; i < BIT_WORD(nr_cpus); ++i)
+		if (cpumask_bits(mask)[i] != ULONG_MAX)
+			return false;
+
+	return !lastmask || (cpumask_bits(mask)[BIT_WORD(nr_cpus)] & lastmask) == lastmask;
 }
 
 static inline int cpumask_weight(const cpumask_t *mask)
diff --git a/lib/on-cpus.c b/lib/on-cpus.c
index aed70f7..8921493 100644
--- a/lib/on-cpus.c
+++ b/lib/on-cpus.c
@@ -124,6 +124,43 @@
 	smp_send_event();
 }
 
+void on_cpumask_async(const cpumask_t *mask, void (*func)(void *data), void *data)
+{
+	int cpu, me = smp_processor_id();
+
+	for_each_cpu(cpu, mask) {
+		if (cpu == me)
+			continue;
+		on_cpu_async(cpu, func, data);
+	}
+	if (cpumask_test_cpu(me, mask))
+		func(data);
+}
+
+void on_cpumask(const cpumask_t *mask, void (*func)(void *data), void *data)
+{
+	int cpu, me = smp_processor_id();
+
+	for_each_cpu(cpu, mask) {
+		if (cpu == me)
+			continue;
+		on_cpu_async(cpu, func, data);
+	}
+	if (cpumask_test_cpu(me, mask))
+		func(data);
+
+	for_each_cpu(cpu, mask) {
+		if (cpu == me)
+			continue;
+		cpumask_set_cpu(me, &on_cpu_info[cpu].waiters);
+		deadlock_check(me, cpu);
+	}
+	while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1)
+		smp_wait_for_event();
+	for_each_cpu(cpu, mask)
+		cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters);
+}
+
 void on_cpu(int cpu, void (*func)(void *data), void *data)
 {
 	on_cpu_async(cpu, func, data);
@@ -132,23 +169,5 @@
 
 void on_cpus(void (*func)(void *data), void *data)
 {
-	int cpu, me = smp_processor_id();
-
-	for_each_present_cpu(cpu) {
-		if (cpu == me)
-			continue;
-		on_cpu_async(cpu, func, data);
-	}
-	func(data);
-
-	for_each_present_cpu(cpu) {
-		if (cpu == me)
-			continue;
-		cpumask_set_cpu(me, &on_cpu_info[cpu].waiters);
-		deadlock_check(me, cpu);
-	}
-	while (cpumask_weight(&cpu_idle_mask) < nr_cpus - 1)
-		smp_wait_for_event();
-	for_each_present_cpu(cpu)
-		cpumask_clear_cpu(me, &on_cpu_info[cpu].waiters);
+	on_cpumask(&cpu_present_mask, func, data);
 }
diff --git a/lib/on-cpus.h b/lib/on-cpus.h
index 41103b0..4bc6236 100644
--- a/lib/on-cpus.h
+++ b/lib/on-cpus.h
@@ -2,6 +2,7 @@
 #ifndef _ON_CPUS_H_
 #define _ON_CPUS_H_
 #include <stdbool.h>
+#include <cpumask.h>
 
 extern bool cpu0_calls_idle;
 
@@ -10,5 +11,7 @@
 void on_cpu_async(int cpu, void (*func)(void *data), void *data);
 void on_cpu(int cpu, void (*func)(void *data), void *data);
 void on_cpus(void (*func)(void *data), void *data);
+void on_cpumask_async(const cpumask_t *mask, void (*func)(void *data), void *data);
+void on_cpumask(const cpumask_t *mask, void (*func)(void *data), void *data);
 
 #endif /* _ON_CPUS_H_ */
diff --git a/lib/riscv/asm/csr.h b/lib/riscv/asm/csr.h
index 24b333e..16f5ddd 100644
--- a/lib/riscv/asm/csr.h
+++ b/lib/riscv/asm/csr.h
@@ -51,7 +51,8 @@
 #define IRQ_S_GEXT		12
 #define IRQ_PMU_OVF		13
 
-#define IE_TIE			(_AC(0x1, UL) << IRQ_S_TIMER)
+#define IE_SSIE			(_AC(1, UL) << IRQ_S_SOFT)
+#define IE_TIE			(_AC(1, UL) << IRQ_S_TIMER)
 
 #define IP_TIP			IE_TIE
 
diff --git a/lib/riscv/asm/processor.h b/lib/riscv/asm/processor.h
index 4c9ad96..4063255 100644
--- a/lib/riscv/asm/processor.h
+++ b/lib/riscv/asm/processor.h
@@ -32,10 +32,26 @@
 	csr_clear(CSR_SSTATUS, SR_SIE);
 }
 
+static inline void local_ipi_enable(void)
+{
+	csr_set(CSR_SIE, IE_SSIE);
+}
+
+static inline void local_ipi_disable(void)
+{
+	csr_clear(CSR_SIE, IE_SSIE);
+}
+
+static inline void ipi_ack(void)
+{
+	csr_clear(CSR_SIP, IE_SSIE);
+}
+
 void install_exception_handler(unsigned long cause, void (*handler)(struct pt_regs *));
 void install_irq_handler(unsigned long cause, void (*handler)(struct pt_regs *));
 void do_handle_exception(struct pt_regs *regs);
 void thread_info_init(void);
+void local_hart_init(void);
 
 void show_regs(struct pt_regs *regs);
 
diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
index 47e9102..e032444 100644
--- a/lib/riscv/asm/sbi.h
+++ b/lib/riscv/asm/sbi.h
@@ -13,10 +13,12 @@
 #define SBI_ERR_ALREADY_STOPPED		-8
 
 #ifndef __ASSEMBLY__
+#include <cpumask.h>
 
 enum sbi_ext_id {
 	SBI_EXT_BASE = 0x10,
 	SBI_EXT_TIME = 0x54494d45,
+	SBI_EXT_IPI = 0x735049,
 	SBI_EXT_HSM = 0x48534d,
 	SBI_EXT_SRST = 0x53525354,
 	SBI_EXT_DBCN = 0x4442434E,
@@ -43,6 +45,10 @@
 	SBI_EXT_TIME_SET_TIMER = 0,
 };
 
+enum sbi_ext_ipi_fid {
+	SBI_EXT_IPI_SEND_IPI = 0,
+};
+
 enum sbi_ext_dbcn_fid {
 	SBI_EXT_DBCN_CONSOLE_WRITE = 0,
 	SBI_EXT_DBCN_CONSOLE_READ,
@@ -61,6 +67,10 @@
 
 void sbi_shutdown(void);
 struct sbiret sbi_hart_start(unsigned long hartid, unsigned long entry, unsigned long sp);
+struct sbiret sbi_send_ipi(unsigned long hart_mask, unsigned long hart_mask_base);
+struct sbiret sbi_send_ipi_cpu(int cpu);
+struct sbiret sbi_send_ipi_cpumask(const cpumask_t *mask);
+struct sbiret sbi_set_timer(unsigned long stime_value);
 long sbi_probe(int ext);
 
 #endif /* !__ASSEMBLY__ */
diff --git a/lib/riscv/asm/timer.h b/lib/riscv/asm/timer.h
index b3514d3..d206a29 100644
--- a/lib/riscv/asm/timer.h
+++ b/lib/riscv/asm/timer.h
@@ -5,6 +5,8 @@
 #include <asm/csr.h>
 
 extern void timer_get_frequency(void);
+extern void timer_start(unsigned long duration_us);
+extern void timer_stop(void);
 
 static inline uint64_t timer_get_cycles(void)
 {
diff --git a/lib/riscv/processor.c b/lib/riscv/processor.c
index 0dffadc..b055acc 100644
--- a/lib/riscv/processor.c
+++ b/lib/riscv/processor.c
@@ -3,10 +3,12 @@
  * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
  */
 #include <libcflat.h>
+#include <limits.h>
 #include <asm/csr.h>
 #include <asm/isa.h>
 #include <asm/processor.h>
 #include <asm/setup.h>
+#include <asm/smp.h>
 
 extern unsigned long ImageBase;
 
@@ -82,3 +84,12 @@
 	isa_init(&cpus[cpu]);
 	csr_write(CSR_SSCRATCH, &cpus[cpu]);
 }
+
+void local_hart_init(void)
+{
+	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
+		csr_write(CSR_STIMECMP, ULONG_MAX);
+		if (__riscv_xlen == 32)
+			csr_write(CSR_STIMECMPH, ULONG_MAX);
+	}
+}
diff --git a/lib/riscv/sbi.c b/lib/riscv/sbi.c
index 3d4236e..ecc63ac 100644
--- a/lib/riscv/sbi.c
+++ b/lib/riscv/sbi.c
@@ -1,6 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include <libcflat.h>
+#include <cpumask.h>
+#include <limits.h>
 #include <asm/sbi.h>
+#include <asm/setup.h>
 
 struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
 			unsigned long arg1, unsigned long arg2,
@@ -39,6 +42,56 @@
 	return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, entry, sp, 0, 0, 0);
 }
 
+struct sbiret sbi_send_ipi(unsigned long hart_mask, unsigned long hart_mask_base)
+{
+	return sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, hart_mask, hart_mask_base, 0, 0, 0, 0);
+}
+
+struct sbiret sbi_send_ipi_cpu(int cpu)
+{
+	return sbi_send_ipi(1UL, cpus[cpu].hartid);
+}
+
+struct sbiret sbi_send_ipi_cpumask(const cpumask_t *mask)
+{
+	struct sbiret ret;
+	cpumask_t tmp;
+
+	if (cpumask_full(mask))
+		return sbi_send_ipi(0, -1UL);
+
+	cpumask_copy(&tmp, mask);
+
+	while (!cpumask_empty(&tmp)) {
+		unsigned long base = ULONG_MAX;
+		unsigned long mask = 0;
+		int cpu;
+
+		for_each_cpu(cpu, &tmp) {
+			if (base > cpus[cpu].hartid)
+				base = cpus[cpu].hartid;
+		}
+
+		for_each_cpu(cpu, &tmp) {
+			if (cpus[cpu].hartid < base + BITS_PER_LONG) {
+				mask |= 1UL << (cpus[cpu].hartid - base);
+				cpumask_clear_cpu(cpu, &tmp);
+			}
+		}
+
+		ret = sbi_send_ipi(mask, base);
+		if (ret.error)
+			break;
+	}
+
+	return ret;
+}
+
+struct sbiret sbi_set_timer(unsigned long stime_value)
+{
+	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
+}
+
 long sbi_probe(int ext)
 {
 	struct sbiret ret;
diff --git a/lib/riscv/setup.c b/lib/riscv/setup.c
index 9a16f00..495db04 100644
--- a/lib/riscv/setup.c
+++ b/lib/riscv/setup.c
@@ -210,6 +210,7 @@
 	cpu_init();
 	timer_get_frequency();
 	thread_info_init();
+	local_hart_init();
 	io_init();
 
 	ret = dt_get_bootargs(&bootargs);
@@ -276,6 +277,7 @@
 	cpu_init();
 	timer_get_frequency();
 	thread_info_init();
+	local_hart_init();
 	io_init();
 	initrd_setup();
 
diff --git a/lib/riscv/smp.c b/lib/riscv/smp.c
index 4d373e0..eb7061a 100644
--- a/lib/riscv/smp.c
+++ b/lib/riscv/smp.c
@@ -27,6 +27,7 @@
 
 	__mmu_enable(data->satp);
 	thread_info_init();
+	local_hart_init();
 	info = current_thread_info();
 	set_cpu_online(info->cpu, true);
 	smp_send_event();
diff --git a/lib/riscv/timer.c b/lib/riscv/timer.c
index d78d254..18063b7 100644
--- a/lib/riscv/timer.c
+++ b/lib/riscv/timer.c
@@ -4,7 +4,13 @@
  */
 #include <libcflat.h>
 #include <devicetree.h>
+#include <limits.h>
+#include <asm/csr.h>
+#include <asm/delay.h>
+#include <asm/isa.h>
+#include <asm/sbi.h>
 #include <asm/setup.h>
+#include <asm/smp.h>
 #include <asm/timer.h>
 
 void timer_get_frequency(void)
@@ -26,3 +32,41 @@
 	data = (u32 *)prop->data;
 	timebase_frequency = fdt32_to_cpu(*data);
 }
+
+void timer_start(unsigned long duration_us)
+{
+	uint64_t next = timer_get_cycles() + usec_to_cycles((uint64_t)duration_us);
+
+	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
+		csr_write(CSR_STIMECMP, (unsigned long)next);
+		if (__riscv_xlen == 32)
+			csr_write(CSR_STIMECMPH, (unsigned long)(next >> 32));
+	} else if (sbi_probe(SBI_EXT_TIME)) {
+		struct sbiret ret = sbi_set_timer(next);
+		assert(ret.error == SBI_SUCCESS);
+		assert(!(next >> 32));
+	} else {
+		assert_msg(false, "No timer to start!");
+	}
+}
+
+void timer_stop(void)
+{
+	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
+		/*
+		 * Subtract one from ULONG_MAX to workaround QEMU using that
+		 * exact number to decide *not* to update the timer. IOW, if
+		 * we used ULONG_MAX, then we wouldn't stop the timer at all,
+		 * but one less is still a big number ("infinity") and it gets
+		 * QEMU to do what we want.
+		 */
+		csr_write(CSR_STIMECMP, ULONG_MAX - 1);
+		if (__riscv_xlen == 32)
+			csr_write(CSR_STIMECMPH, ULONG_MAX - 1);
+	} else if (sbi_probe(SBI_EXT_TIME)) {
+		struct sbiret ret = sbi_set_timer(ULONG_MAX);
+		assert(ret.error == SBI_SUCCESS);
+	} else {
+		assert_msg(false, "No timer to stop!");
+	}
+}
diff --git a/riscv/sbi.c b/riscv/sbi.c
index 36ddfd4..093c20a 100644
--- a/riscv/sbi.c
+++ b/riscv/sbi.c
@@ -15,7 +15,6 @@
 #include <asm/csr.h>
 #include <asm/delay.h>
 #include <asm/io.h>
-#include <asm/isa.h>
 #include <asm/mmu.h>
 #include <asm/processor.h>
 #include <asm/sbi.h>
@@ -30,19 +29,21 @@
 	puts("An environ must be provided where expected values are given.\n");
 }
 
-static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0)
+static struct sbiret sbi_base(int fid, unsigned long arg0)
 {
 	return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0);
 }
 
-static struct sbiret __time_sbi_ecall(unsigned long stime_value)
+static struct sbiret sbi_dbcn_write(unsigned long num_bytes, unsigned long base_addr_lo,
+				    unsigned long base_addr_hi)
 {
-	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
+	return sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE,
+			 num_bytes, base_addr_lo, base_addr_hi, 0, 0, 0);
 }
 
-static struct sbiret __dbcn_sbi_ecall(int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2)
+static struct sbiret sbi_dbcn_write_byte(uint8_t byte)
 {
-	return sbi_ecall(SBI_EXT_DBCN, fid, arg0, arg1, arg2, 0, 0, 0);
+	return sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, byte, 0, 0, 0, 0, 0);
 }
 
 static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo)
@@ -103,7 +104,7 @@
 
 	report_prefix_push("base");
 
-	ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0);
+	ret = sbi_base(SBI_EXT_BASE_GET_SPEC_VERSION, 0);
 	if (ret.error || ret.value < 2) {
 		report_skip("SBI spec version 0.2 or higher required");
 		return;
@@ -119,7 +120,7 @@
 	report_prefix_push("impl_id");
 	if (env_or_skip("SBI_IMPL_ID")) {
 		expected = (long)strtoul(getenv("SBI_IMPL_ID"), NULL, 0);
-		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0);
+		ret = sbi_base(SBI_EXT_BASE_GET_IMP_ID, 0);
 		gen_report(&ret, 0, expected);
 	}
 	report_prefix_pop();
@@ -127,17 +128,17 @@
 	report_prefix_push("impl_version");
 	if (env_or_skip("SBI_IMPL_VERSION")) {
 		expected = (long)strtoul(getenv("SBI_IMPL_VERSION"), NULL, 0);
-		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0);
+		ret = sbi_base(SBI_EXT_BASE_GET_IMP_VERSION, 0);
 		gen_report(&ret, 0, expected);
 	}
 	report_prefix_pop();
 
 	report_prefix_push("probe_ext");
 	expected = getenv("SBI_PROBE_EXT") ? (long)strtoul(getenv("SBI_PROBE_EXT"), NULL, 0) : 1;
-	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE);
+	ret = sbi_base(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE);
 	gen_report(&ret, 0, expected);
 	report_prefix_push("unavailable");
-	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000);
+	ret = sbi_base(SBI_EXT_BASE_PROBE_EXT, 0xb000000);
 	gen_report(&ret, 0, 0);
 	report_prefix_pop();
 	report_prefix_pop();
@@ -146,7 +147,7 @@
 	if (env_or_skip("MVENDORID")) {
 		expected = (long)strtoul(getenv("MVENDORID"), NULL, 0);
 		assert(__riscv_xlen == 32 || !(expected >> 32));
-		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0);
+		ret = sbi_base(SBI_EXT_BASE_GET_MVENDORID, 0);
 		gen_report(&ret, 0, expected);
 	}
 	report_prefix_pop();
@@ -154,7 +155,7 @@
 	report_prefix_push("marchid");
 	if (env_or_skip("MARCHID")) {
 		expected = (long)strtoul(getenv("MARCHID"), NULL, 0);
-		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0);
+		ret = sbi_base(SBI_EXT_BASE_GET_MARCHID, 0);
 		gen_report(&ret, 0, expected);
 	}
 	report_prefix_pop();
@@ -162,7 +163,7 @@
 	report_prefix_push("mimpid");
 	if (env_or_skip("MIMPID")) {
 		expected = (long)strtoul(getenv("MIMPID"), NULL, 0);
-		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0);
+		ret = sbi_base(SBI_EXT_BASE_GET_MIMPID, 0);
 		gen_report(&ret, 0, expected);
 	}
 	report_prefix_pop();
@@ -198,7 +199,7 @@
 	if (timer_info.mask_timer_irq)
 		timer_irq_disable();
 	else
-		__time_sbi_ecall(ULONG_MAX);
+		sbi_set_timer(ULONG_MAX);
 
 	if (!timer_irq_pending())
 		timer_info.timer_irq_cleared = true;
@@ -217,7 +218,7 @@
 
 	timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq };
 	begin = timer_get_cycles();
-	ret = __time_sbi_ecall(begin + d);
+	ret = sbi_set_timer(begin + d);
 
 	report(!ret.error, "set timer%s", mask_test_str);
 	if (ret.error)
@@ -258,11 +259,6 @@
 
 	install_irq_handler(IRQ_S_TIMER, timer_irq_handler);
 	local_irq_enable();
-	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
-		csr_write(CSR_STIMECMP, ULONG_MAX);
-		if (__riscv_xlen == 32)
-			csr_write(CSR_STIMECMPH, ULONG_MAX);
-	}
 	timer_irq_enable();
 
 	timer_check_set_timer(false);
@@ -273,10 +269,10 @@
 		report_skip("timer irq enable bit is not writable, skipping mask irq test");
 
 	timer_irq_disable();
-	__time_sbi_ecall(0);
+	sbi_set_timer(0);
 	pending = timer_irq_pending();
 	report(pending, "timer immediately pending by setting timer to 0");
-	__time_sbi_ecall(ULONG_MAX);
+	sbi_set_timer(ULONG_MAX);
 	if (pending)
 		report(!timer_irq_pending(), "pending timer cleared while masked");
 	else
@@ -302,7 +298,7 @@
 	split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
 
 	do {
-		ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, num_bytes, base_addr_lo, base_addr_hi);
+		ret = sbi_dbcn_write(num_bytes, base_addr_lo, base_addr_hi);
 		num_bytes -= ret.value;
 		paddr += ret.value;
 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
@@ -335,7 +331,7 @@
 
 /*
  * Only the write functionality is tested here. There's no easy way to
- * non-interactively test the read functionality.
+ * non-interactively test SBI_EXT_DBCN_CONSOLE_READ.
  */
 static void check_dbcn(void)
 {
@@ -349,8 +345,7 @@
 
 	report_prefix_push("dbcn");
 
-	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_DBCN);
-	if (!ret.value) {
+	if (!sbi_probe(SBI_EXT_DBCN)) {
 		report_skip("DBCN extension unavailable");
 		report_prefix_pop();
 		return;
@@ -403,7 +398,7 @@
 
 	if (do_invalid_addr) {
 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
-		ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, 1, base_addr_lo, base_addr_hi);
+		ret = sbi_dbcn_write(1, base_addr_lo, base_addr_hi);
 		report(ret.error == SBI_ERR_INVALID_PARAM, "address (error=%ld)", ret.error);
 	}
 	report_prefix_pop();
@@ -411,12 +406,19 @@
 	report_prefix_pop();
 	report_prefix_push("write_byte");
 
-	puts("DBCN_WRITE TEST CHAR: ");
-	ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, (u8)DBCN_WRITE_BYTE_TEST_BYTE, 0, 0);
+	puts("DBCN_WRITE_BYTE TEST BYTE: ");
+	ret = sbi_dbcn_write_byte(DBCN_WRITE_BYTE_TEST_BYTE);
 	puts("\n");
 	report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error);
 	report(ret.value == 0, "expected ret.value (%ld)", ret.value);
 
+	puts("DBCN_WRITE_BYTE TEST WORD: "); /* still expect 'a' in the output */
+	ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, 0x64636261, 0, 0, 0, 0, 0);
+	puts("\n");
+	report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error);
+	report(ret.value == 0, "expected ret.value (%ld)", ret.value);
+
+	report_prefix_pop();
 	report_prefix_pop();
 }