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 */