Merge patch series "xtensa: Enable qemu-xtensa board"
Jiaxun Yang <jiaxun.yang@flygoat.com> says:
Hi all,
This series enabled qemu-xtensa board.
For dc232b CPU it needs to be built with toolchain[1].
This is a side product of me investigating architectures
physical address != virtual address in U-Boot. Now we can
get it covered under CI and regular tests.
VirtIO devices are not working as expected, due to U-Boot's
assumption on VA == PA everywhere, I'm going to get this fixed
later.
My Xtensa knowledge is pretty limited, Xtensa people please
feel free to point out if I got anything wrong.
Thanks
[1]: https://github.com/foss-xtensa/toolchain/releases/download/2020.07/x86_64-2020.07-xtensa-dc232b-elf.tar.gz
diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index fff7878..cf1ace4 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -237,7 +237,7 @@
# the below corresponds to .gitlab-ci.yml "before_script"
cd \${WORK_DIR}
git config --global --add safe.directory \${WORK_DIR}
- git clone --depth=1 https://source.denx.de/u-boot/u-boot-test-hooks /tmp/uboot-test-hooks
+ git clone --depth=1 https://github.com/FlyGoat/u-boot-test-hooks.git /tmp/uboot-test-hooks
ln -s travis-ci /tmp/uboot-test-hooks/bin/\`hostname\`
ln -s travis-ci /tmp/uboot-test-hooks/py/\`hostname\`
grub-mkimage --prefix=\"\" -o ~/grub_x86.efi -O i386-efi normal echo lsefimmap lsefi lsefisystab efinet tftp minicmd
@@ -457,6 +457,9 @@
qemu_x86_64:
TEST_PY_BD: "qemu-x86_64"
TEST_PY_TEST_SPEC: "not sleep"
+ qemu_xtensa_dc233c:
+ TEST_PY_BD: "qemu-xtensa-dc233c"
+ TEST_PY_TEST_SPEC: "not sleep and not efi"
r2dplus_i82557c:
TEST_PY_BD: "r2dplus"
TEST_PY_ID: "--id i82557c_qemu"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a6c5040..0a15b73 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -422,6 +422,12 @@
TEST_PY_TEST_SPEC: "not sleep"
<<: *buildman_and_testpy_dfn
+qemu-xtensa-dc233c test.py:
+ variables:
+ TEST_PY_BD: "qemu-xtensa-dc233c"
+ TEST_PY_TEST_SPEC: "not sleep and not efi"
+ <<: *buildman_and_testpy_dfn
+
r2dplus_i82557c test.py:
variables:
TEST_PY_BD: "r2dplus"
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 8f668cc..70eebb4 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -15,6 +15,10 @@
bool "Support XTFPGA"
select BOARD_POSTCLK_INIT
+config TARGET_QEMU_XTENSA
+ bool "Support QEMU Xtensa Virt Board"
+ select BOARD_LATE_INIT
+
endchoice
config SYS_ICACHE_OFF
@@ -41,6 +45,34 @@
help
Do not enable data cache in SPL.
+config XTENSA_SEMIHOSTING
+ bool "Support semihosting"
+ help
+ Enable Xtensa semihosting debugging support.
+
+choice
+ prompt "Semihosting interface"
+ default XTENSA_SIMCALL_ISS
+ depends on XTENSA_SEMIHOSTING
+ help
+ Choose semihosting interface that will be used for serial port,
+ block device and networking.
+
+config XTENSA_SIMCALL_ISS
+ bool "simcall"
+ help
+ Use simcall instruction. simcall is only available on simulators,
+ it does nothing on hardware.
+
+config XTENSA_SIMCALL_GDBIO
+ bool "GDBIO"
+ help
+ Use break instruction. It is available on real hardware when GDB
+ is attached to it via JTAG.
+
+endchoice
+
source "board/cadence/xtfpga/Kconfig"
+source "board/emulation/qemu-xtensa/Kconfig"
endmenu
diff --git a/arch/xtensa/config.mk b/arch/xtensa/config.mk
index b080999..200b66f 100644
--- a/arch/xtensa/config.mk
+++ b/arch/xtensa/config.mk
@@ -7,3 +7,9 @@
-ffunction-sections -fdata-sections
LDFLAGS_FINAL += --gc-sections
+
+ifeq ($(CONFIG_SYS_BIG_ENDIAN),y)
+PLATFORM_CPPFLAGS += -B xtensa -O elf32-xtensa-be
+else
+PLATFORM_ELFFLAGS += -B xtensa -O elf32-xtensa-le
+endif
diff --git a/arch/xtensa/cpu/cpu.c b/arch/xtensa/cpu/cpu.c
index abcd8f7..d226681 100644
--- a/arch/xtensa/cpu/cpu.c
+++ b/arch/xtensa/cpu/cpu.c
@@ -48,8 +48,3 @@
gd->ram_size = CFG_SYS_SDRAM_SIZE;
return 0;
}
-
-int dram_init(void)
-{
- return 0;
-}
diff --git a/arch/xtensa/cpu/u-boot.lds b/arch/xtensa/cpu/u-boot.lds
index 84ba32c..72e4b9a 100644
--- a/arch/xtensa/cpu/u-boot.lds
+++ b/arch/xtensa/cpu/u-boot.lds
@@ -82,6 +82,8 @@
__reloc_end = .;
__init_end = .;
+ /* Calculation to get end address in ROM */
+ _end = LOADADDR(.data) + (_data_end - _data_start);
SECTION_bss(__init_end (OVERLAY),)
diff --git a/arch/xtensa/dts/virt-u-boot.dtsi b/arch/xtensa/dts/virt-u-boot.dtsi
new file mode 100644
index 0000000..22b4683
--- /dev/null
+++ b/arch/xtensa/dts/virt-u-boot.dtsi
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#include <config.h>
+
+/ {
+ memory@0 {
+ device_type = "memory";
+#if XCHAL_HAVE_PTP_MMU
+ reg = <0x00000000 CFG_SYS_SDRAM_SIZE>;
+#else
+ reg = <CFG_SYS_MEMORY_BASE CFG_SYS_SDRAM_SIZE>;
+#endif
+ };
+};
diff --git a/arch/xtensa/include/asm/addrspace.h b/arch/xtensa/include/asm/addrspace.h
index 920b5fd..c8bed88 100644
--- a/arch/xtensa/include/asm/addrspace.h
+++ b/arch/xtensa/include/asm/addrspace.h
@@ -7,6 +7,8 @@
#ifndef _XTENSA_ADDRSPACE_H
#define _XTENSA_ADDRSPACE_H
+#include <config.h>
+
#include <asm/arch/core.h>
/*
diff --git a/arch/xtensa/include/asm/io.h b/arch/xtensa/include/asm/io.h
index 87ad9fa..ab2438b 100644
--- a/arch/xtensa/include/asm/io.h
+++ b/arch/xtensa/include/asm/io.h
@@ -12,6 +12,8 @@
#include <linux/types.h>
#include <asm/byteorder.h>
+#include <asm/addrspace.h>
+
/*
* swap functions to change byte order from little-endian to big-endian and
* vice versa.
@@ -127,6 +129,36 @@
{
}
+#if XCHAL_HAVE_PTP_MMU
+static inline void *phys_to_virt(phys_addr_t paddr)
+{
+ if (paddr >= CFG_SYS_IO_BASE)
+ return (void *)(unsigned long)paddr;
+
+ if (paddr < CFG_MAX_MEM_MAPPED)
+ return (void *)(unsigned long)MEMADDR(paddr);
+
+ return NULL;
+}
+
+#define phys_to_virt phys_to_virt
+
+static inline phys_addr_t virt_to_phys(void *vaddr)
+{
+ unsigned long addr = (unsigned long)vaddr;
+
+ if (addr >= CFG_SYS_IO_BASE)
+ return addr;
+
+ if (addr >= CFG_SYS_SDRAM_BASE && addr < MEMADDR(CFG_MAX_MEM_MAPPED))
+ return PHYSADDR(addr);
+
+ return 0;
+}
+
+#define virt_to_phys virt_to_phys
+#endif /* XCHAL_HAVE_PTP_MMU */
+
#include <asm-generic/io.h>
#endif /* _XTENSA_IO_H */
diff --git a/arch/xtensa/include/asm/ldscript.h b/arch/xtensa/include/asm/ldscript.h
index 78a0b23..bcf0fd5 100644
--- a/arch/xtensa/include/asm/ldscript.h
+++ b/arch/xtensa/include/asm/ldscript.h
@@ -165,7 +165,6 @@
. = ALIGN(8); \
_bss_end = ABSOLUTE(.); \
__bss_end = ABSOLUTE(.); \
- _end = ALIGN(0x8); \
PROVIDE(end = ALIGN(0x8)); \
_stack_sentry = ALIGN(0x8); \
}
diff --git a/arch/xtensa/include/asm/platform/simcall-gdbio.h b/arch/xtensa/include/asm/platform/simcall-gdbio.h
new file mode 100644
index 0000000..e642860
--- /dev/null
+++ b/arch/xtensa/include/asm/platform/simcall-gdbio.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021 Cadence Design Systems Inc. */
+
+#ifndef _XTENSA_PLATFORM_ISS_SIMCALL_GDBIO_H
+#define _XTENSA_PLATFORM_ISS_SIMCALL_GDBIO_H
+
+/*
+ * System call like services offered by the GDBIO host.
+ */
+
+#define SYS_open -2
+#define SYS_close -3
+#define SYS_read -4
+#define SYS_write -5
+#define SYS_lseek -6
+
+static int errno;
+
+static inline int __simc(int a, int b, int c, int d)
+{
+ register int a1 asm("a2") = a;
+ register int b1 asm("a6") = b;
+ register int c1 asm("a3") = c;
+ register int d1 asm("a4") = d;
+ __asm__ __volatile__ (
+ "break 1, 14\n"
+ : "+r"(a1), "+r"(c1)
+ : "r"(b1), "r"(d1)
+ : "memory");
+ errno = c1;
+ return a1;
+}
+
+#endif /* _XTENSA_PLATFORM_ISS_SIMCALL_GDBIO_H */
diff --git a/arch/xtensa/include/asm/platform/simcall-iss.h b/arch/xtensa/include/asm/platform/simcall-iss.h
new file mode 100644
index 0000000..5a1e7a1
--- /dev/null
+++ b/arch/xtensa/include/asm/platform/simcall-iss.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021 Cadence Design Systems Inc. */
+
+#ifndef _XTENSA_PLATFORM_ISS_SIMCALL_ISS_H
+#define _XTENSA_PLATFORM_ISS_SIMCALL_ISS_H
+
+/*
+ * System call like services offered by the simulator host.
+ */
+
+#define SYS_nop 0 /* unused */
+#define SYS_exit 1 /*x*/
+#define SYS_fork 2
+#define SYS_read 3 /*x*/
+#define SYS_write 4 /*x*/
+#define SYS_open 5 /*x*/
+#define SYS_close 6 /*x*/
+#define SYS_rename 7 /*x 38 - waitpid */
+#define SYS_creat 8 /*x*/
+#define SYS_link 9 /*x (not implemented on WIN32) */
+#define SYS_unlink 10 /*x*/
+#define SYS_execv 11 /* n/a - execve */
+#define SYS_execve 12 /* 11 - chdir */
+#define SYS_pipe 13 /* 42 - time */
+#define SYS_stat 14 /* 106 - mknod */
+#define SYS_chmod 15
+#define SYS_chown 16 /* 202 - lchown */
+#define SYS_utime 17 /* 30 - break */
+#define SYS_wait 18 /* n/a - oldstat */
+#define SYS_lseek 19 /*x*/
+#define SYS_getpid 20
+#define SYS_isatty 21 /* n/a - mount */
+#define SYS_fstat 22 /* 108 - oldumount */
+#define SYS_time 23 /* 13 - setuid */
+#define SYS_gettimeofday 24 /*x 78 - getuid (not implemented on WIN32) */
+#define SYS_times 25 /*X 43 - stime (Xtensa-specific implementation) */
+#define SYS_socket 26
+#define SYS_sendto 27
+#define SYS_recvfrom 28
+#define SYS_select_one 29 /* not compatible select, one file descriptor at the time */
+#define SYS_bind 30
+#define SYS_ioctl 31
+
+#define SYS_iss_argc 1000 /* returns value of argc */
+#define SYS_iss_argv_size 1001 /* bytes needed for argv & arg strings */
+#define SYS_iss_set_argv 1002 /* saves argv & arg strings at given addr */
+
+/*
+ * SYS_select_one specifiers
+ */
+
+#define XTISS_SELECT_ONE_READ 1
+#define XTISS_SELECT_ONE_WRITE 2
+#define XTISS_SELECT_ONE_EXCEPT 3
+
+static int errno;
+
+static inline int __simc(int a, int b, int c, int d)
+{
+ register int a1 asm("a2") = a;
+ register int b1 asm("a3") = b;
+ register int c1 asm("a4") = c;
+ register int d1 asm("a5") = d;
+ __asm__ __volatile__ (
+ "simcall\n"
+ : "+r"(a1), "+r"(b1)
+ : "r"(c1), "r"(d1)
+ : "memory");
+ errno = b1;
+ return a1;
+}
+
+#endif /* _XTENSA_PLATFORM_ISS_SIMCALL_ISS_H */
diff --git a/arch/xtensa/include/asm/platform/simcall.h b/arch/xtensa/include/asm/platform/simcall.h
new file mode 100644
index 0000000..a1cb269
--- /dev/null
+++ b/arch/xtensa/include/asm/platform/simcall.h
@@ -0,0 +1,110 @@
+/*
+ * include/asm-xtensa/platform-iss/simcall.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 Tensilica Inc.
+ * Copyright (C) 2017 - 2021 Cadence Design Systems Inc.
+ */
+
+#ifndef _XTENSA_PLATFORM_ISS_SIMCALL_H
+#define _XTENSA_PLATFORM_ISS_SIMCALL_H
+
+#include <linux/bug.h>
+
+#ifdef CONFIG_XTENSA_SIMCALL_ISS
+#include <asm/platform/simcall-iss.h>
+#endif
+#ifdef CONFIG_XTENSA_SIMCALL_GDBIO
+#include <asm/platform/simcall-gdbio.h>
+#endif
+
+static inline int simc_exit(int exit_code)
+{
+#ifdef SYS_exit
+ return __simc(SYS_exit, exit_code, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return -1;
+#endif
+}
+
+static inline int simc_open(const char *file, int flags, int mode)
+{
+ return __simc(SYS_open, (int) file, flags, mode);
+}
+
+static inline int simc_close(int fd)
+{
+ return __simc(SYS_close, fd, 0, 0);
+}
+
+static inline int simc_ioctl(int fd, int request, void *arg)
+{
+#ifdef SYS_ioctl
+ return __simc(SYS_ioctl, fd, request, (int) arg);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return -1;
+#endif
+}
+
+static inline int simc_read(int fd, void *buf, size_t count)
+{
+ return __simc(SYS_read, fd, (int) buf, count);
+}
+
+static inline int simc_write(int fd, const void *buf, size_t count)
+{
+ return __simc(SYS_write, fd, (int) buf, count);
+}
+
+static inline int simc_poll(int fd)
+{
+#ifdef SYS_select_one
+ long timeval[2] = { 0, 0 };
+
+ return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&timeval);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return -1;
+#endif
+}
+
+static inline int simc_lseek(int fd, uint32_t off, int whence)
+{
+ return __simc(SYS_lseek, fd, off, whence);
+}
+
+static inline int simc_argc(void)
+{
+#ifdef SYS_iss_argc
+ return __simc(SYS_iss_argc, 0, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return 0;
+#endif
+}
+
+static inline int simc_argv_size(void)
+{
+#ifdef SYS_iss_argv_size
+ return __simc(SYS_iss_argv_size, 0, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return 0;
+#endif
+}
+
+static inline void simc_argv(void *buf)
+{
+#ifdef SYS_iss_set_argv
+ __simc(SYS_iss_set_argv, (int)buf, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+#endif
+}
+
+#endif /* _XTENSA_PLATFORM_ISS_SIMCALL_H */
diff --git a/board/cadence/xtfpga/xtfpga.c b/board/cadence/xtfpga/xtfpga.c
index 5110fed..6b92fe3 100644
--- a/board/cadence/xtfpga/xtfpga.c
+++ b/board/cadence/xtfpga/xtfpga.c
@@ -66,6 +66,11 @@
#endif
}
+int dram_init(void)
+{
+ return 0;
+}
+
int board_postclk_init(void)
{
gd->cpu_clk = get_board_sys_clk();
diff --git a/board/emulation/qemu-xtensa/Kconfig b/board/emulation/qemu-xtensa/Kconfig
new file mode 100644
index 0000000..8767b6f
--- /dev/null
+++ b/board/emulation/qemu-xtensa/Kconfig
@@ -0,0 +1,43 @@
+if TARGET_QEMU_XTENSA
+
+config SYS_BOARD
+ default "qemu-xtensa"
+
+config SYS_VENDOR
+ default "emulation"
+
+config SYS_CONFIG_NAME
+ default "qemu-xtensa"
+
+config TEXT_BASE
+ default 0x50000000 if (SYS_CPU = de212)
+ default 0xfe000000
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+ def_bool y
+ select BOARD_EARLY_INIT_F
+ select DM
+ select CPU
+ select CPU_XTENSA
+ select CLK
+ select DM_SERIAL
+ select XTENSA_SEMIHOSTING
+ select XTENSA_SEMIHOSTING_SERIAL
+ imply BLK
+ imply VIRTIO
+ imply VIRTIO_PCI
+ imply VIRTIO_NET
+ imply VIRTIO_BLK
+ imply E1000
+ imply PCI
+ imply PCI_INIT_R
+ imply NVME_PCI
+ imply PCIE_ECAM_GENERIC
+ imply SCSI
+ imply REMAKE_ELF
+ select OF_CONTROL
+ select OF_UPSTREAM
+ imply CMD_DM
+ imply CMD_PCI
+
+endif
diff --git a/board/emulation/qemu-xtensa/MAINTAINERS b/board/emulation/qemu-xtensa/MAINTAINERS
new file mode 100644
index 0000000..6ffdfe8
--- /dev/null
+++ b/board/emulation/qemu-xtensa/MAINTAINERS
@@ -0,0 +1,8 @@
+QEMU XTENSA 'VIRT' BOARD
+M: Jiaxun Yang <jiaxun.yang@flygoat.com>
+M: Max Filippov <jcmvbkbc@gmail.com>
+S: Maintained
+F: board/emulation/qemu-xtensa/
+F: board/emulation/common/
+F: include/configs/qemu-xtensa.h
+F: configs/qemu-xtensa-dc233c_defconfig
diff --git a/board/emulation/qemu-xtensa/Makefile b/board/emulation/qemu-xtensa/Makefile
new file mode 100644
index 0000000..0f33a65
--- /dev/null
+++ b/board/emulation/qemu-xtensa/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2024, Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+obj-y += qemu-xtensa.o
diff --git a/board/emulation/qemu-xtensa/qemu-xtensa.c b/board/emulation/qemu-xtensa/qemu-xtensa.c
new file mode 100644
index 0000000..0ca8334
--- /dev/null
+++ b/board/emulation/qemu-xtensa/qemu-xtensa.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <dm.h>
+#include <cpu.h>
+#include <log.h>
+#include <init.h>
+#include <usb.h>
+#include <virtio_types.h>
+#include <virtio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int board_init(void)
+{
+ return 0;
+}
+
+unsigned long get_board_sys_clk(void)
+{
+ return gd->cpu_clk ? gd->cpu_clk : 40000000;
+}
+
+int dram_init(void)
+{
+ return fdtdec_setup_mem_size_base();
+}
+
+int board_early_init_f(void)
+{
+ struct cpu_plat *cpu_plat;
+ struct udevice *cpu = cpu_get_current_dev();
+
+ if (!cpu)
+ return -ENODEV;
+
+ cpu_plat = dev_get_parent_plat(cpu);
+ if (!cpu_plat)
+ return -ENODEV;
+
+ gd->cpu_clk = cpu_plat->timebase_freq;
+ return 0;
+}
+
+int board_late_init(void)
+{
+ /* start usb so that usb keyboard can be used as input device */
+ if (CONFIG_IS_ENABLED(USB_KEYBOARD))
+ usb_init();
+
+ /*
+ * Make sure virtio bus is enumerated so that peripherals
+ * on the virtio bus can be discovered by their drivers
+ */
+ virtio_init();
+
+ return 0;
+}
diff --git a/configs/qemu-xtensa-dc233c_defconfig b/configs/qemu-xtensa-dc233c_defconfig
new file mode 100644
index 0000000..e6f40b3
--- /dev/null
+++ b/configs/qemu-xtensa-dc233c_defconfig
@@ -0,0 +1,32 @@
+CONFIG_XTENSA=y
+CONFIG_SYS_CPU="dc233c"
+CONFIG_SYS_MALLOC_LEN=0x40000
+CONFIG_SYS_MALLOC_F_LEN=0x400
+CONFIG_ENV_SIZE=0x20000
+CONFIG_DEFAULT_DEVICE_TREE="virt"
+CONFIG_SYS_MONITOR_LEN=262144
+CONFIG_SYS_LOAD_ADDR=0x02000000
+CONFIG_TARGET_QEMU_XTENSA=y
+CONFIG_REMAKE_ELF=y
+CONFIG_SYS_MONITOR_BASE=0xF6000000
+CONFIG_DYNAMIC_SYS_CLK_FREQ=y
+CONFIG_SHOW_BOOT_PROGRESS=y
+CONFIG_BOOTDELAY=10
+CONFIG_AUTOBOOT_KEYED=y
+CONFIG_AUTOBOOT_PROMPT="Autobooting in %d seconds, press <SPACE> to stop\n"
+CONFIG_AUTOBOOT_STOP_STR=" "
+CONFIG_SYS_PBSIZE=1049
+CONFIG_SYS_MALLOC_BOOTPARAMS=y
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot> "
+CONFIG_CMD_ASKENV=y
+CONFIG_CRC32_VERIFY=y
+CONFIG_CMD_MX_CYCLIC=y
+CONFIG_CMD_SAVES=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_DIAG=y
+CONFIG_USE_BOOTFILE=y
+CONFIG_BOOTFILE="uImage"
+CONFIG_VERSION_VARIABLE=y
+CONFIG_SYSRESET=y
diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst
index d3d6b8f..98a0b26 100644
--- a/doc/board/emulation/index.rst
+++ b/doc/board/emulation/index.rst
@@ -14,3 +14,4 @@
qemu-ppce500
qemu-riscv
qemu-x86
+ qemu-xtensa
diff --git a/doc/board/emulation/qemu-xtensa.rst b/doc/board/emulation/qemu-xtensa.rst
new file mode 100644
index 0000000..fff23c1
--- /dev/null
+++ b/doc/board/emulation/qemu-xtensa.rst
@@ -0,0 +1,33 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+
+QEMU Xtensa
+===========
+
+QEMU for Xtensa supports a special 'virt' machine designed for emulation and
+virtualization purposes. This document describes how to run U-Boot under it.
+
+The QEMU virt machine models a generic Xtensa virtual machine with PCI Bus
+and Xtensa ISS simcall semihosting support. It supports many different Xtensa
+CPU configuration. Currently, only dc233c variant is tested against U-Boot.
+
+Building U-Boot
+---------------
+Set the CROSS_COMPILE environment variable as usual, and run:
+
+ make qemu-xtensa-dc233c_defconfig
+ make
+
+Note that Xtensa's toolchain is bounded to CPU configuration, you must use
+the toolchain built for exactly the same CPU configuration as you selected
+in U-Boot.
+
+Running U-Boot
+--------------
+The minimal QEMU command line to get U-Boot up and running is:
+
+ qemu-system-xtensa -nographic -machine virt -cpu dc233c -semihosting -kernel ./u-boot.elf
+
+You many change cpu option to match your U-Boot CPU type configuration.
+semihosting option is mandatory because this is the only way to interact
+with U-Boot in command line.
diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig
index 1c3c810..5c06cd9 100644
--- a/drivers/cpu/Kconfig
+++ b/drivers/cpu/Kconfig
@@ -33,3 +33,9 @@
select XILINX_MICROBLAZE0_PVR
help
Support CPU cores for Microblaze architecture.
+
+config CPU_XTENSA
+ bool "Enable Xtensa CPU driver"
+ depends on CPU && XTENSA
+ help
+ Support CPU cores for Xtensa architecture.
diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile
index d4bbf6f..bc75d9b 100644
--- a/drivers/cpu/Makefile
+++ b/drivers/cpu/Makefile
@@ -14,4 +14,5 @@
obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o
obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o
obj-$(CONFIG_CPU_MICROBLAZE) += microblaze_cpu.o
+obj-$(CONFIG_CPU_XTENSA) += xtensa_cpu.o
obj-$(CONFIG_SANDBOX) += cpu_sandbox.o
diff --git a/drivers/cpu/xtensa_cpu.c b/drivers/cpu/xtensa_cpu.c
new file mode 100644
index 0000000..fbb561d
--- /dev/null
+++ b/drivers/cpu/xtensa_cpu.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#include <clk.h>
+#include <cpu.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#include <asm/arch/core.h>
+
+static int xtensa_cpu_get_desc(const struct udevice *dev, char *buf, int size)
+{
+ const char *cpu = XCHAL_CORE_ID;
+
+ if (!cpu || size < (strlen(cpu) + 1))
+ return -ENOSPC;
+
+ strcpy(buf, cpu);
+
+ return 0;
+}
+
+static int xtensa_cpu_get_info(const struct udevice *dev, struct cpu_info *info)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+
+ info->cpu_freq = plat->timebase_freq;
+
+#if XCHAL_HAVE_PTP_MMU
+ info->features |= BIT(CPU_FEAT_MMU);
+#endif
+#if XCHAL_ICACHE_SIZE || XCHAL_DCACHE_SIZE
+ info->features |= BIT(CPU_FEAT_L1_CACHE);
+#endif
+
+ return 0;
+}
+
+static int xtensa_cpu_get_count(const struct udevice *dev)
+{
+ ofnode node;
+ int num = 0;
+
+ ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
+ const char *device_type;
+
+ /* skip if hart is marked as not available in the device tree */
+ if (!ofnode_is_enabled(node))
+ continue;
+
+ device_type = ofnode_read_string(node, "device_type");
+ if (!device_type)
+ continue;
+ if (strcmp(device_type, "cpu") == 0)
+ num++;
+ }
+
+ return num;
+}
+
+static int xtensa_cpu_bind(struct udevice *dev)
+{
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+
+ plat->cpu_id = dev_read_addr(dev);
+
+ return 0;
+}
+
+static int xtensa_cpu_probe(struct udevice *dev)
+{
+ int ret = 0;
+ struct clk clk;
+ struct cpu_plat *plat = dev_get_parent_plat(dev);
+
+ asm volatile ("rsr %0, 176\n"
+ "rsr %1, 208\n"
+ : "=r"(plat->id[0]), "=r"(plat->id[1]));
+
+ /* Get a clock if it exists */
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (!ret) {
+ ret = clk_enable(&clk);
+ if (ret && (ret != -ENOSYS || ret != -ENOTSUPP))
+ return ret;
+ ret = clk_get_rate(&clk);
+ if (!IS_ERR_VALUE(ret))
+ plat->timebase_freq = ret;
+ }
+
+ return 0;
+}
+
+static const struct cpu_ops xtensa_cpu_ops = {
+ .get_desc = xtensa_cpu_get_desc,
+ .get_info = xtensa_cpu_get_info,
+ .get_count = xtensa_cpu_get_count,
+};
+
+static const struct udevice_id xtensa_cpu_ids[] = {
+ { .compatible = "cdns,xtensa-cpu" },
+ { }
+};
+
+U_BOOT_DRIVER(xtensa_cpu) = {
+ .name = "xtensa_cpu",
+ .id = UCLASS_CPU,
+ .of_match = xtensa_cpu_ids,
+ .bind = xtensa_cpu_bind,
+ .probe = xtensa_cpu_probe,
+ .ops = &xtensa_cpu_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 1fe4607..3a1e5a6 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -501,6 +501,15 @@
driver will be available until the real driver model serial is
running.
+config DEBUG_UART_XTENSA_SEMIHOSTING
+ bool "Xtensa semihosting"
+ depends on XTENSA_SEMIHOSTING_SERIAL
+ help
+ Select this to enable the debug UART using the Xtensa semihosting driver.
+ This provides basic serial output from the console without needing to
+ start up driver model. The driver will be available until the real
+ driver model serial is running.
+
endchoice
config DEBUG_UART_BASE
@@ -936,7 +945,6 @@
config SEMIHOSTING_SERIAL
bool "Semihosting UART support"
depends on SEMIHOSTING && !SERIAL_RX_BUFFER
- imply SERIAL_PUTS
help
Select this to enable a serial UART using semihosting. Special halt
instructions will be issued which an external debugger (such as a
@@ -1115,6 +1123,14 @@
If built without DM support, then requires Xen
to be built with CONFIG_VERBOSE_DEBUG.
+config XTENSA_SEMIHOSTING_SERIAL
+ bool "Xtensa Semihosting UART support"
+ depends on DM_SERIAL
+ depends on XTENSA_SEMIHOSTING
+ imply SERIAL_PUTS
+ help
+ Select this to enable a serial UART using Xtensa semihosting.
+
choice
prompt "Console port"
default 8xx_CONS_SMC1
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index dbe598b..78810f9 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -60,6 +60,7 @@
obj-$(CONFIG_HTIF_CONSOLE) += serial_htif.o
obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
+obj-$(CONFIG_XTENSA_SEMIHOSTING_SERIAL) += serial_xtensa_semihosting.o
obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
ifndef CONFIG_SPL_BUILD
diff --git a/drivers/serial/serial_xtensa_semihosting.c b/drivers/serial/serial_xtensa_semihosting.c
new file mode 100644
index 0000000..0e59a9b
--- /dev/null
+++ b/drivers/serial/serial_xtensa_semihosting.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <serial.h>
+
+#include <asm/platform/simcall.h>
+
+/**
+ * struct simc_serial_priv - Semihosting serial private data
+ * @counter: Counter used to fake pending every other call
+ */
+struct simc_serial_priv {
+ unsigned int counter;
+};
+
+static int simc_serial_getc(struct udevice *dev)
+{
+ char ch = 0;
+
+ simc_read(0, &ch, sizeof(ch));
+
+ return ch;
+}
+
+static int simc_serial_putc(struct udevice *dev, const char ch)
+{
+ char str[2] = {0};
+
+ str[0] = ch;
+ simc_write(1, str, 1);
+
+ return 0;
+}
+
+static int simc_serial_pending(struct udevice *dev, bool input)
+{
+ struct simc_serial_priv *priv = dev_get_priv(dev);
+
+ if (input) {
+ int res = simc_poll(0);
+ return res < 0 ? priv->counter++ & 1 : res;
+ }
+
+ return false;
+}
+
+static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len)
+{
+ int ret;
+
+ ret = simc_write(1, s, len);
+
+ return ret;
+}
+
+static const struct dm_serial_ops simc_serial_ops = {
+ .putc = simc_serial_putc,
+ .puts = smh_serial_puts,
+ .getc = simc_serial_getc,
+ .pending = simc_serial_pending,
+};
+
+U_BOOT_DRIVER(simc_serial) = {
+ .name = "serial_xtensa_semihosting",
+ .id = UCLASS_SERIAL,
+ .priv_auto = sizeof(struct simc_serial_priv),
+ .ops = &simc_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRVINFO(simc_serial) = {
+ .name = "serial_xtensa_semihosting",
+};
+
+#if CONFIG_IS_ENABLED(DEBUG_UART_XTENSA_SEMIHOSTING)
+#include <debug_uart.h>
+
+static inline void _debug_uart_init(void)
+{
+}
+
+static inline void _debug_uart_putc(int c)
+{
+ simc_serial_putc(NULL, c);
+}
+
+DEBUG_UART_FUNCS
+#endif
diff --git a/dts/upstream/src/xtensa/Makefile b/dts/upstream/src/xtensa/Makefile
new file mode 100644
index 0000000..2a81acb
--- /dev/null
+++ b/dts/upstream/src/xtensa/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+include $(srctree)/scripts/Makefile.dts
+
+targets += $(dtb-y)
+
+# Add any required device tree compiler flags here
+DTC_FLAGS += -a 0x8
+
+PHONY += dtbs
+dtbs: $(addprefix $(obj)/, $(dtb-y))
+ @:
+
+clean-files := *.dtb *.dtbo */*.dtb */*.dtbo
diff --git a/include/configs/qemu-xtensa.h b/include/configs/qemu-xtensa.h
new file mode 100644
index 0000000..505aa06
--- /dev/null
+++ b/include/configs/qemu-xtensa.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2007-2013 Tensilica, Inc.
+ * Copyright (C) 2014 - 2016 Cadence Design Systems Inc.
+ * Copyright (C) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include <asm/arch/core.h>
+#include <asm/addrspace.h>
+#include <asm/config.h>
+
+#if XCHAL_HAVE_PTP_MMU
+#define CFG_SYS_MEMORY_BASE \
+ (XCHAL_VECBASE_RESET_VADDR - XCHAL_VECBASE_RESET_PADDR)
+#define CFG_SYS_IO_BASE 0xf0000000
+#define CFG_SYS_SDRAM_SIZE 0x80000000 /* xtensa.sysram0 */
+#else
+#define CFG_SYS_MEMORY_BASE 0x60000000
+#define CFG_SYS_SDRAM_SIZE 0x08000000 /* xtensa.sysram0 */
+#endif
+
+#define CFG_SYS_SDRAM_BASE MEMADDR(0x00000000)
+
+#if defined(CFG_MAX_MEM_MAPPED) && \
+ CFG_MAX_MEM_MAPPED < CFG_SYS_SDRAM_SIZE
+#define XTENSA_SYS_TEXT_ADDR \
+ (MEMADDR(CFG_MAX_MEM_MAPPED) - CONFIG_SYS_MONITOR_LEN)
+#else
+#define XTENSA_SYS_TEXT_ADDR \
+ (MEMADDR(CFG_SYS_SDRAM_SIZE) - CONFIG_SYS_MONITOR_LEN)
+#endif
+
+#endif /* __CONFIG_H */