Merge patch series "m68k: Add support for QEMU virt machine"

Kuan-Wei Chiu <visitorckw@gmail.com> says:

Add support for the QEMU 'virt' machine on the m68k architecture. The
QEMU virt machine models a generic system utilizing Goldfish virtual
peripherals and is capable of emulating various classic 68k CPUs.

Currently, U-Boot's m68k architecture support focuses on ColdFire
variants. This series expands support to include the classic M680x0
architecture, implementing the necessary exception vectors, startup
code, and a bootinfo parser compatible with the QEMU interface.

Drivers for Goldfish peripherals (TTY, Timer, RTC) and the QEMU
Virtual System Controller (sysreset) are also added to enable serial
console, timekeeping, and system reset functionality.

The implementation has been verified on QEMU targeting the M68040 CPU,
confirming successful hardware initialization and boot to the U-Boot
command shell. Additionally, the CI configuration was verified locally
using gitlab-ci-local "qemu_m68k_virt test.py", resulting in
PASS qemu_m68k_virt test.py.

Link: https://lore.kernel.org/r/20260107201838.3448806-1-visitorckw@gmail.com
[trini: Re-sort MAINTAINERS entries]
Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
Tom Rini
2026-02-02 14:24:56 -06:00
34 changed files with 915 additions and 21 deletions

View File

@@ -524,6 +524,9 @@ stages:
TEST_PY_ID: "--id qemu" TEST_PY_ID: "--id qemu"
TEST_PY_TEST_SPEC: "not sleep and not efi" TEST_PY_TEST_SPEC: "not sleep and not efi"
OVERRIDE: "-a CONFIG_M68K_QEMU=y -a ~CONFIG_MCFTMR" OVERRIDE: "-a CONFIG_M68K_QEMU=y -a ~CONFIG_MCFTMR"
qemu_m68k_virt:
TEST_PY_BD: "qemu-m68k"
TEST_PY_TEST_SPEC: "not sleep"
qemu_malta: qemu_malta:
TEST_PY_BD: "malta" TEST_PY_BD: "malta"
TEST_PY_ID: "--id qemu" TEST_PY_ID: "--id qemu"

View File

@@ -421,6 +421,12 @@ qemu_m68k test.py:
OVERRIDE: "-a CONFIG_M68K_QEMU=y -a ~CONFIG_MCFTMR" OVERRIDE: "-a CONFIG_M68K_QEMU=y -a ~CONFIG_MCFTMR"
<<: *buildman_and_testpy_dfn <<: *buildman_and_testpy_dfn
qemu_m68k_virt test.py:
variables:
TEST_PY_BD: "qemu-m68k"
TEST_PY_TEST_SPEC: "not sleep"
<<: *buildman_and_testpy_dfn
qemu_malta test.py: qemu_malta test.py:
variables: variables:
TEST_PY_BD: "malta" TEST_PY_BD: "malta"

View File

@@ -1048,15 +1048,6 @@ T: git https://source.denx.de/u-boot/custodians/u-boot-clk.git
F: drivers/clk/ F: drivers/clk/
F: drivers/clk/imx/ F: drivers/clk/imx/
COLDFIRE
M: Huan Wang <alison.wang@nxp.com>
M: Angelo Dureghello <angelo@kernel-space.org>
S: Maintained
T: git https://source.denx.de/u-boot/custodians/u-boot-coldfire.git
F: arch/m68k/
F: doc/arch/m68k.rst
F: drivers/watchdog/mcf_wdt.c
CPU CPU
M: Simon Glass <sjg@chromium.org> M: Simon Glass <sjg@chromium.org>
M: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> M: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
@@ -1263,6 +1254,18 @@ S: Maintained
F: drivers/misc/gsc.c F: drivers/misc/gsc.c
F: include/gsc.h F: include/gsc.h
GOLDFISH SERIAL DRIVER
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
F: drivers/serial/serial_goldfish.c
F: include/goldfish_tty.h
GOLDFISH TIMER DRIVER
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
F: drivers/timer/goldfish_timer.c
F: include/goldfish_timer.h
INTERCONNECT: INTERCONNECT:
M: Neil Armstrong <neil.armstrong@linaro.org> M: Neil Armstrong <neil.armstrong@linaro.org>
S: Maintained S: Maintained
@@ -1315,6 +1318,21 @@ F: lib/getopt.c
F: test/log/ F: test/log/
F: test/py/tests/test_log.py F: test/py/tests/test_log.py
M680X0 ARCHITECTURE
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
F: arch/m68k/cpu/m680x0/
F: arch/m68k/include/asm/bootinfo.h
M68K
M: Angelo Dureghello <angelo@kernel-space.org>
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
T: git https://source.denx.de/u-boot/custodians/u-boot-coldfire.git
F: arch/m68k/
F: doc/arch/m68k.rst
F: drivers/watchdog/mcf_wdt.c
MALI DISPLAY PROCESSORS MALI DISPLAY PROCESSORS
M: Liviu Dudau <liviu.dudau@foss.arm.com> M: Liviu Dudau <liviu.dudau@foss.arm.com>
S: Supported S: Supported
@@ -1589,6 +1607,12 @@ S: Maintained
T: git https://source.denx.de/u-boot/custodians/u-boot-mpc85xx.git T: git https://source.denx.de/u-boot/custodians/u-boot-mpc85xx.git
F: arch/powerpc/cpu/mpc85xx/ F: arch/powerpc/cpu/mpc85xx/
QEMU VIRTUAL SYSTEM CONTROLLER
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
F: drivers/sysreset/sysreset_qemu_virt_ctrl.c
F: include/qemu_virt_ctrl.h
RAW NAND RAW NAND
M: Dario Binacchi <dario.binacchi@amarulasolutions.com> M: Dario Binacchi <dario.binacchi@amarulasolutions.com>
M: Michael Trimarchi <michael@amarulasolutions.com> M: Michael Trimarchi <michael@amarulasolutions.com>

View File

@@ -12,50 +12,64 @@ config MCF520x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF52x2 config MCF52x2
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF523x config MCF523x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF530x config MCF530x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF5301x config MCF5301x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF532x config MCF532x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF537x config MCF537x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config MCF5441x config MCF5441x
select OF_CONTROL select OF_CONTROL
select DM select DM
select DM_SERIAL select DM_SERIAL
select ARCH_COLDFIRE
bool bool
config M680x0
bool
help
This enables support for the classic Motorola 68000 family of
processors.
# processor type # processor type
config M5208 config M5208
bool bool
@@ -110,6 +124,10 @@ config M54418
bool bool
select MCF5441x select MCF5441x
config M68040
bool
select M680x0
# peripherals # peripherals
config CF_DSPI config CF_DSPI
bool bool
@@ -176,8 +194,28 @@ config TARGET_STMARK2
select CF_DSPI select CF_DSPI
select M54418 select M54418
config TARGET_QEMU_M68K
bool "Support QEMU m68k virt"
select M68040
imply CMD_DM
help
This target supports the QEMU m68k virtual machine (-M virt).
It simulates a Motorola 68040 CPU with Goldfish peripherals.
endchoice endchoice
config SYS_CPU
string
default "mcf52x2" if MCF52x2
default "mcf523x" if MCF523x
default "mcf530x" if MCF530x
default "mcf532x" if MCF532x
default "mcf5445x" if MCF5445x
default "m680x0" if M680x0
config ARCH_COLDFIRE
bool
source "board/BuS/eb_cpu5282/Kconfig" source "board/BuS/eb_cpu5282/Kconfig"
source "board/cobra5272/Kconfig" source "board/cobra5272/Kconfig"
source "board/nxp/m5208evbe/Kconfig" source "board/nxp/m5208evbe/Kconfig"
@@ -192,6 +230,7 @@ source "board/nxp/m5329evb/Kconfig"
source "board/nxp/m5373evb/Kconfig" source "board/nxp/m5373evb/Kconfig"
source "board/sysam/amcore/Kconfig" source "board/sysam/amcore/Kconfig"
source "board/sysam/stmark2/Kconfig" source "board/sysam/stmark2/Kconfig"
source "board/emulation/qemu-m68k/Kconfig"
config M68K_QEMU config M68K_QEMU
bool "Build with workarounds for incomplete QEMU emulation" bool "Build with workarounds for incomplete QEMU emulation"

View File

@@ -17,6 +17,7 @@ cpuflags-$(CONFIG_M5307) := -mcpu=5307
cpuflags-$(CONFIG_MCF5301x) := -mcpu=53015 -fPIC cpuflags-$(CONFIG_MCF5301x) := -mcpu=53015 -fPIC
cpuflags-$(CONFIG_MCF532x) := -mcpu=5329 -fPIC cpuflags-$(CONFIG_MCF532x) := -mcpu=5329 -fPIC
cpuflags-$(CONFIG_MCF5441x) := -mcpu=54418 -fPIC cpuflags-$(CONFIG_MCF5441x) := -mcpu=54418 -fPIC
cpuflags-$(CONFIG_M68040) := -mcpu=68040 -fno-pic
PLATFORM_CPPFLAGS += $(cpuflags-y) PLATFORM_CPPFLAGS += $(cpuflags-y)

View File

@@ -3,8 +3,14 @@
# (C) Copyright 2000-2002 # (C) Copyright 2000-2002
# Wolfgang Denk, DENX Software Engineering, wd@denx.de. # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
PLATFORM_CPPFLAGS += -D__M68K__ -fPIC PLATFORM_CPPFLAGS += -D__M68K__
ifneq ($(CONFIG_M680x0),y)
PLATFORM_CPPFLAGS += -fPIC
endif
KBUILD_LDFLAGS += -n -pie KBUILD_LDFLAGS += -n -pie
PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections
PLATFORM_RELFLAGS += -ffixed-d7 -msep-data PLATFORM_RELFLAGS += -ffixed-d7
ifneq ($(CONFIG_M680x0),y)
PLATFORM_RELFLAGS += -msep-data
endif
LDFLAGS_FINAL += --gc-sections -pie LDFLAGS_FINAL += --gc-sections -pie

View File

@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
extra-y += start.o
obj-y += cpu.o

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CPU specific code for m68040
*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#include <config.h>
#include <cpu_func.h>
#include <init.h>
#include <stdio.h>
#include <asm/global_data.h>
#include <linux/types.h>
DECLARE_GLOBAL_DATA_PTR;
void m68k_virt_init_reserve(ulong base)
{
struct global_data *gd_ptr = (struct global_data *)base;
char *p = (char *)gd_ptr;
unsigned int i;
/* FIXME: usage of memset() here caused a hang on QEMU m68k virt. */
for (i = 0; i < sizeof(*gd_ptr); i++)
p[i] = 0;
gd = gd_ptr;
gd->malloc_base = base + sizeof(*gd_ptr);
}
int print_cpuinfo(void)
{
puts("CPU: M68040 (QEMU Virt)\n");
return 0;
}
int get_clocks(void)
{
return 0;
}
int cpu_init_r(void)
{
return 0;
}
/*
* Relocation Stub
* We skip actual relocation for this QEMU bring-up and jump directly
* to board_init_r.
*/
void relocate_code(ulong sp, struct global_data *new_gd, ulong relocaddr)
{
board_init_r(new_gd, relocaddr);
}
/* Stubs for Standard Facilities (Cache, Interrupts) */
int disable_interrupts(void) { return 0; }
void enable_interrupts(void) { return; }
int interrupt_init(void) { return 0; }
void icache_enable(void) {}
void icache_disable(void) {}
int icache_status(void) { return 0; }
void dcache_enable(void) {}
void dcache_disable(void) {}
int dcache_status(void) { return 0; }
void flush_cache(unsigned long start, unsigned long size) {}
void flush_dcache_range(unsigned long start, unsigned long stop) {}

View File

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Startup code for m68040
*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#include <asm-offsets.h>
#include <config.h>
#include <linux/linkage.h>
.section .text
/*
* Vector Table
* m68k uses the first 1KB for the exception vector table.
*/
.balign 4
.global _vectors
_vectors:
.long CFG_SYS_INIT_SP_ADDR /* 0x00: Initial SP */
.long _start /* 0x04: Initial PC (Reset) */
.long _fault /* 0x08: Bus Error */
.long _fault /* 0x0C: Address Error */
.long _fault /* 0x10: Illegal Instruction */
.long _fault /* 0x14: Zero Divide */
.long _fault /* 0x18: CHK */
.long _fault /* 0x1C: TRAPV */
.long _fault /* 0x20: Privilege */
.long _fault /* 0x24: Trace */
.long _fault /* 0x28: Line 1010 */
.long _fault /* 0x2C: Line 1111 */
.fill 0x400 - (.-_vectors), 1, 0
/*
* Entry Point
*/
ENTRY(_start)
/* Disable Interrupts */
move.w #0x2700, %sr
/* Setup initial stack pointer */
move.l #CFG_SYS_INIT_SP_ADDR, %sp
/*
* Allocate Global Data (GD)
* board_init_f_alloc_reserve(top) returns the new top of stack in %d0
*/
move.l %sp, -(%sp)
bsr.l board_init_f_alloc_reserve
addq.l #4, %sp
/* Update Stack Pointer and set GD register */
move.l %d0, %sp
move.l %d0, %d7 /* %d7 is the gd register */
/* Initialize Reserved Memory. */
move.l %d0, -(%sp)
bsr.l m68k_virt_init_reserve
addq.l #4, %sp
/* Enter board_init_f(0) */
clr.l -(%sp)
bsr.l board_init_f
addq.l #4, %sp
/* Should not return */
hang:
bra.s hang
ENDPROC(_start)
_fault:
bra.s _fault

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linker Script for m68040
*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
OUTPUT_ARCH(m68k)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
__text_start = .;
.text :
{
arch/m68k/cpu/m680x0/start.o (.text*)
*(.text*)
}
. = ALIGN(16);
.rodata : { *(.rodata*) }
. = ALIGN(16);
.data : { *(.data*) }
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(*u_boot_list*)));
}
. = ALIGN(4);
__image_copy_end = .;
__init_end = .;
. = ALIGN(16);
__bss_start = .;
.bss :
{
*(.bss*)
. = ALIGN(16);
}
__bss_end = .;
_end = .;
}

View File

@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*
* Definitions for the m68k bootinfo interface.
*/
#ifndef _ASM_M68K_BOOTINFO_H
#define _ASM_M68K_BOOTINFO_H
#ifndef __ASSEMBLY__
struct bi_record {
unsigned short tag; /* tag ID */
unsigned short size; /* size of record (in bytes) */
unsigned long data[0]; /* data */
};
#endif /* __ASSEMBLY__ */
/* Bootinfo Tag IDs */
#define BI_LAST 0x0000
#define BI_MACHTYPE 0x0001
#define BI_CPUTYPE 0x0002
#define BI_FPUTYPE 0x0003
#define BI_MMUTYPE 0x0004
#define BI_MEMCHUNK 0x0005
#define BI_RAMDISK 0x0006
#define BI_COMMAND_LINE 0x0007
/* QEMU virt specific tags */
#define BI_VIRT_QEMU_VERSION 0x8000
#define BI_VIRT_GF_PIC_BASE 0x8001
#define BI_VIRT_GF_RTC_BASE 0x8002
#define BI_VIRT_GF_TTY_BASE 0x8003
#define BI_VIRT_VIRTIO_BASE 0x8004
#define BI_VIRT_CTRL_BASE 0x8005
#endif /* _ASM_M68K_BOOTINFO_H */

View File

@@ -7,10 +7,5 @@
## if the user asked for it ## if the user asked for it
lib-$(CONFIG_USE_PRIVATE_LIBGCC) += lshrdi3.o muldi3.o ashldi3.o ashrdi3.o lib-$(CONFIG_USE_PRIVATE_LIBGCC) += lshrdi3.o muldi3.o ashldi3.o ashrdi3.o
obj-y += bdinfo.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o obj-$(CONFIG_CMD_BOOTM) += bootm.o
obj-y += cache.o obj-$(CONFIG_ARCH_COLDFIRE) += cache.o interrupts.o time.o traps.o bdinfo.o fec.o
obj-y += interrupts.o
obj-y += time.o
obj-y += traps.o
obj-y += fec.o

View File

@@ -0,0 +1,12 @@
if TARGET_QEMU_M68K
config SYS_BOARD
default "qemu-m68k"
config SYS_VENDOR
default "emulation"
config SYS_CONFIG_NAME
default "qemu-m68k"
endif

View File

@@ -0,0 +1,8 @@
QEMU M68K VIRT BOARD
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
F: board/emulation/qemu-m68k/
F: board/emulation/common/
F: include/configs/qemu-m68k.h
F: configs/qemu-m68k_defconfig
F: doc/board/emulation/qemu-m68k.rst

View File

@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
obj-y += qemu-m68k.o

View File

@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#include <config.h>
#include <goldfish_rtc.h>
#include <goldfish_timer.h>
#include <goldfish_tty.h>
#include <init.h>
#include <qemu_virt_ctrl.h>
#include <serial.h>
#include <asm-generic/sections.h>
#include <asm/bootinfo.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <dm/platdata.h>
#include <linux/errno.h>
#include <linux/sizes.h>
DECLARE_GLOBAL_DATA_PTR;
static struct goldfish_tty_plat serial_plat;
static struct goldfish_rtc_plat rtc_plat;
static struct goldfish_timer_plat timer_plat;
static struct qemu_virt_ctrl_plat reset_plat;
/*
* Theoretical limit derivation:
* Max Bootinfo Size (Standard Page) = 4096 bytes
* Min Record Size (Tag + Size) = 4 bytes
* Max Records = 4096 / 4 = 1024
*/
#define MAX_BOOTINFO_RECORDS 1024
static void parse_bootinfo(void)
{
struct bi_record *record;
ulong addr;
int loops = 0;
/* QEMU places bootinfo after _end, aligned to 2 bytes */
addr = (ulong)&_end;
addr = ALIGN(addr, 2);
record = (struct bi_record *)addr;
if (record->tag != BI_MACHTYPE)
return;
while (record->tag != BI_LAST) {
phys_addr_t base = record->data[0];
if (++loops > MAX_BOOTINFO_RECORDS)
panic("Bootinfo loop exceeded");
switch (record->tag) {
case BI_VIRT_GF_TTY_BASE:
serial_plat.reg = base;
break;
case BI_VIRT_GF_RTC_BASE:
rtc_plat.reg = base;
timer_plat.reg = base;
break;
case BI_VIRT_CTRL_BASE:
reset_plat.reg = base;
break;
case BI_MEMCHUNK:
gd->ram_size = record->data[1];
break;
}
record = (struct bi_record *)((ulong)record + record->size);
}
}
int board_early_init_f(void)
{
parse_bootinfo();
return 0;
}
int checkboard(void)
{
puts("Board: QEMU m68k virt\n");
return 0;
}
int dram_init(void)
{
/* Default: 16MB */
if (!gd->ram_size)
gd->ram_size = SZ_16M;
return 0;
}
U_BOOT_DRVINFO(goldfish_rtc) = {
.name = "rtc_goldfish",
.plat = &rtc_plat,
};
U_BOOT_DRVINFO(goldfish_timer) = {
.name = "goldfish_timer",
.plat = &timer_plat,
};
U_BOOT_DRVINFO(goldfish_serial) = {
.name = "serial_goldfish",
.plat = &serial_plat,
};
U_BOOT_DRVINFO(sysreset_qemu_virt_ctrl) = {
.name = "sysreset_qemu_virt_ctrl",
.plat = &reset_plat,
};

View File

@@ -0,0 +1,20 @@
CONFIG_M68K=y
CONFIG_TEXT_BASE=0x00000000
CONFIG_SYS_MALLOC_LEN=0x20000
CONFIG_SYS_MALLOC_F_LEN=0x2000
CONFIG_SYS_MONITOR_LEN=262144
CONFIG_SYS_BOOTM_LEN=0x1000000
CONFIG_SYS_LOAD_ADDR=0x00000000
CONFIG_TARGET_QEMU_M68K=y
# CONFIG_DISPLAY_BOARDINFO is not set
CONFIG_BOARD_EARLY_INIT_F=y
CONFIG_CMD_POWEROFF=y
CONFIG_DM_RTC=y
CONFIG_RTC_GOLDFISH=y
CONFIG_DM_SERIAL=y
CONFIG_SERIAL_GOLDFISH=y
CONFIG_SYSRESET=y
CONFIG_SYSRESET_CMD_POWEROFF=y
CONFIG_SYSRESET_QEMU_VIRT_CTRL=y
CONFIG_TIMER=y
CONFIG_GOLDFISH_TIMER=y

View File

@@ -17,6 +17,7 @@ Emulation
qemu-sbsa qemu-sbsa
qemu-x86 qemu-x86
qemu-xtensa qemu-xtensa
qemu-m68k
Also see Also see

View File

@@ -0,0 +1,39 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
.. Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
QEMU m68k
=========
QEMU for m68k 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 m68k virtual machine with Goldfish
interfaces. It supports the Motorola 68040 CPU architecture.
Building U-Boot
---------------
Set the CROSS_COMPILE environment variable to your m68k toolchain, and run:
.. code-block:: bash
export CROSS_COMPILE=m68k-linux-gnu-
make qemu-m68k_defconfig
make
Running U-Boot
--------------
The minimal QEMU command line to get U-Boot up and running is:
.. code-block:: bash
qemu-system-m68k -M virt -cpu m68040 -nographic -kernel u-boot
Note that the `-nographic` option is used to redirect the console to stdio,
which connects to the emulated Goldfish TTY device.
Hardware Support
----------------
The following QEMU virt peripherals are supported in U-Boot:
* Goldfish TTY (Serial Console)
* Goldfish RTC (Real Time Clock)

View File

@@ -9,6 +9,7 @@
#include <div64.h> #include <div64.h>
#include <dm.h> #include <dm.h>
#include <goldfish_rtc.h>
#include <mapmem.h> #include <mapmem.h>
#include <rtc.h> #include <rtc.h>
#include <linux/io.h> #include <linux/io.h>
@@ -74,15 +75,27 @@ static int goldfish_rtc_set(struct udevice *dev, const struct rtc_time *time)
return 0; return 0;
} }
static int goldfish_rtc_probe(struct udevice *dev) static int goldfish_rtc_of_to_plat(struct udevice *dev)
{ {
struct goldfish_rtc *priv = dev_get_priv(dev); struct goldfish_rtc_plat *plat = dev_get_plat(dev);
fdt_addr_t addr; fdt_addr_t addr;
addr = dev_read_addr(dev); addr = dev_read_addr(dev);
if (addr == FDT_ADDR_T_NONE) if (addr != FDT_ADDR_T_NONE)
plat->reg = addr;
return 0;
}
static int goldfish_rtc_probe(struct udevice *dev)
{
struct goldfish_rtc_plat *plat = dev_get_plat(dev);
struct goldfish_rtc *priv = dev_get_priv(dev);
if (!plat->reg)
return -EINVAL; return -EINVAL;
priv->base = map_sysmem(addr, 0x20);
priv->base = map_sysmem(plat->reg, 0x20);
return 0; return 0;
} }
@@ -103,5 +116,7 @@ U_BOOT_DRIVER(rtc_goldfish) = {
.ops = &goldfish_rtc_ops, .ops = &goldfish_rtc_ops,
.probe = goldfish_rtc_probe, .probe = goldfish_rtc_probe,
.of_match = goldfish_rtc_of_match, .of_match = goldfish_rtc_of_match,
.of_to_plat = goldfish_rtc_of_to_plat,
.plat_auto = sizeof(struct goldfish_rtc_plat),
.priv_auto = sizeof(struct goldfish_rtc), .priv_auto = sizeof(struct goldfish_rtc),
}; };

View File

@@ -1193,4 +1193,12 @@ config SYS_SDMR
depends on MPC8XX_CONS depends on MPC8XX_CONS
default 0x0 default 0x0
config SERIAL_GOLDFISH
bool "Goldfish TTY support"
depends on DM_SERIAL
help
Select this to enable support for the Goldfish TTY serial port.
This virtual device is commonly used by QEMU virtual machines
(e.g. m68k virt) for console output.
endif endif

View File

@@ -63,3 +63,4 @@ obj-$(CONFIG_XTENSA_SEMIHOSTING_SERIAL) += serial_xtensa_semihosting.o
obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o
obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o
obj-$(CONFIG_SERIAL_GOLDFISH) += serial_goldfish.o

View File

@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
* Goldfish TTY driver for U-Boot
*/
#include <dm.h>
#include <goldfish_tty.h>
#include <mapmem.h>
#include <serial.h>
#include <asm/io.h>
#include <linux/types.h>
/* Goldfish TTY Register Offsets */
#define GOLDFISH_TTY_PUT_CHAR 0x00
#define GOLDFISH_TTY_BYTES_READY 0x04
#define GOLDFISH_TTY_CMD 0x08
#define GOLDFISH_TTY_DATA_PTR 0x10
#define GOLDFISH_TTY_DATA_LEN 0x14
#define GOLDFISH_TTY_DATA_PTR_HIGH 0x18
#define GOLDFISH_TTY_VERSION 0x20
/* Commands */
#define CMD_WRITE_BUFFER 2
#define CMD_READ_BUFFER 3
struct goldfish_tty_priv {
void __iomem *base;
u8 rx_buf;
};
static int goldfish_serial_getc(struct udevice *dev)
{
struct goldfish_tty_priv *priv = dev_get_priv(dev);
unsigned long paddr;
u32 count;
count = __raw_readl(priv->base + GOLDFISH_TTY_BYTES_READY);
if (count == 0)
return -EAGAIN;
paddr = virt_to_phys((void *)&priv->rx_buf);
__raw_writel(0, priv->base + GOLDFISH_TTY_DATA_PTR_HIGH);
__raw_writel(paddr, priv->base + GOLDFISH_TTY_DATA_PTR);
__raw_writel(1, priv->base + GOLDFISH_TTY_DATA_LEN);
__raw_writel(CMD_READ_BUFFER, priv->base + GOLDFISH_TTY_CMD);
return priv->rx_buf;
}
static int goldfish_serial_putc(struct udevice *dev, const char ch)
{
struct goldfish_tty_priv *priv = dev_get_priv(dev);
__raw_writel(ch, priv->base + GOLDFISH_TTY_PUT_CHAR);
return 0;
}
static int goldfish_serial_pending(struct udevice *dev, bool input)
{
struct goldfish_tty_priv *priv = dev_get_priv(dev);
if (input)
return __raw_readl(priv->base + GOLDFISH_TTY_BYTES_READY);
return 0;
}
static int goldfish_serial_of_to_plat(struct udevice *dev)
{
struct goldfish_tty_plat *plat = dev_get_plat(dev);
fdt_addr_t addr;
addr = dev_read_addr(dev);
if (addr != FDT_ADDR_T_NONE)
plat->reg = addr;
return 0;
}
static int goldfish_serial_probe(struct udevice *dev)
{
struct goldfish_tty_plat *plat = dev_get_plat(dev);
struct goldfish_tty_priv *priv = dev_get_priv(dev);
if (!plat->reg)
return -EINVAL;
priv->base = map_sysmem(plat->reg, 0x1000);
return 0;
}
static const struct dm_serial_ops goldfish_serial_ops = {
.putc = goldfish_serial_putc,
.pending = goldfish_serial_pending,
.getc = goldfish_serial_getc,
};
static const struct udevice_id goldfish_serial_ids[] = {
{ .compatible = "google,goldfish-tty" },
{ }
};
U_BOOT_DRIVER(serial_goldfish) = {
.name = "serial_goldfish",
.id = UCLASS_SERIAL,
.of_match = goldfish_serial_ids,
.of_to_plat = goldfish_serial_of_to_plat,
.plat_auto = sizeof(struct goldfish_tty_plat),
.probe = goldfish_serial_probe,
.ops = &goldfish_serial_ops,
.priv_auto = sizeof(struct goldfish_tty_priv),
.flags = DM_FLAG_PRE_RELOC,
};

View File

@@ -298,6 +298,14 @@ config SYSRESET_QCOM_PSHOLD
help help
Add support for the system reboot on Qualcomm SoCs via PSHOLD. Add support for the system reboot on Qualcomm SoCs via PSHOLD.
config SYSRESET_QEMU_VIRT_CTRL
bool "QEMU Virtual System Controller support"
depends on SYSRESET
help
Enable support for the QEMU Virtual System Controller.
This device is used in QEMU machines (e.g. m68k virt) to trigger
system reset and poweroff events.
endif endif
endmenu endmenu

View File

@@ -32,3 +32,4 @@ obj-$(CONFIG_$(PHASE_)SYSRESET_X86) += sysreset_x86.o
obj-$(CONFIG_SYSRESET_RAA215300) += sysreset_raa215300.o obj-$(CONFIG_SYSRESET_RAA215300) += sysreset_raa215300.o
obj-$(CONFIG_SYSRESET_QCOM_PSHOLD) += sysreset_qcom-pshold.o obj-$(CONFIG_SYSRESET_QCOM_PSHOLD) += sysreset_qcom-pshold.o
obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o
obj-$(CONFIG_SYSRESET_QEMU_VIRT_CTRL) += sysreset_qemu_virt_ctrl.o

View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*
* QEMU Virtual System Controller Driver
*/
#include <dm.h>
#include <qemu_virt_ctrl.h>
#include <sysreset.h>
#include <asm/io.h>
#include <linux/err.h>
/* Register offsets */
#define VIRT_CTRL_REG_FEATURES 0x00
#define VIRT_CTRL_REG_CMD 0x04
/* Commands */
#define VIRT_CTRL_CMD_NOOP 0x00
#define VIRT_CTRL_CMD_RESET 0x01
#define VIRT_CTRL_CMD_HALT 0x02
#define VIRT_CTRL_CMD_PANIC 0x03
static int qemu_virt_ctrl_request(struct udevice *dev, enum sysreset_t type)
{
struct qemu_virt_ctrl_plat *plat = dev_get_plat(dev);
u32 val;
switch (type) {
case SYSRESET_WARM:
case SYSRESET_COLD:
val = VIRT_CTRL_CMD_RESET;
break;
case SYSRESET_POWER_OFF:
val = VIRT_CTRL_CMD_HALT;
break;
default:
return -EPROTONOSUPPORT;
}
writel(val, plat->reg + VIRT_CTRL_REG_CMD);
return -EINPROGRESS;
}
static struct sysreset_ops qemu_virt_ctrl_ops = {
.request = qemu_virt_ctrl_request,
};
U_BOOT_DRIVER(sysreset_qemu_virt_ctrl) = {
.name = "sysreset_qemu_virt_ctrl",
.id = UCLASS_SYSRESET,
.ops = &qemu_virt_ctrl_ops,
.plat_auto = sizeof(struct qemu_virt_ctrl_plat),
};

View File

@@ -340,4 +340,12 @@ config STARFIVE_TIMER
Select this to enable support for the timer found on Select this to enable support for the timer found on
Starfive SoC. Starfive SoC.
config GOLDFISH_TIMER
bool "Goldfish Timer support"
depends on TIMER
help
Select this to enable support for the Goldfish Timer.
It uses the Goldfish RTC hardware to provide a nanosecond-resolution
timer, commonly found in QEMU virt machines.
endmenu endmenu

View File

@@ -36,3 +36,4 @@ obj-$(CONFIG_MCHP_PIT64B_TIMER) += mchp-pit64b-timer.o
obj-$(CONFIG_IMX_GPT_TIMER) += imx-gpt-timer.o obj-$(CONFIG_IMX_GPT_TIMER) += imx-gpt-timer.o
obj-$(CONFIG_XILINX_TIMER) += xilinx-timer.o obj-$(CONFIG_XILINX_TIMER) += xilinx-timer.o
obj-$(CONFIG_STARFIVE_TIMER) += starfive-timer.o obj-$(CONFIG_STARFIVE_TIMER) += starfive-timer.o
obj-$(CONFIG_GOLDFISH_TIMER) += goldfish_timer.o

View File

@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*
* Goldfish Timer driver
*/
#include <dm.h>
#include <goldfish_timer.h>
#include <mapmem.h>
#include <timer.h>
#include <asm/io.h>
#include <linux/errno.h>
struct goldfish_timer_priv {
void __iomem *base;
};
/* Goldfish RTC registers used as Timer */
#define TIMER_TIME_LOW 0x00
#define TIMER_TIME_HIGH 0x04
static u64 goldfish_timer_get_count(struct udevice *dev)
{
struct goldfish_timer_priv *priv = dev_get_priv(dev);
u32 low, high;
u64 time;
/*
* TIMER_TIME_HIGH is only updated when TIMER_TIME_LOW is read.
* We must read LOW before HIGH to latch the high 32-bit value
* and ensure a consistent 64-bit timestamp.
*/
low = readl(priv->base + TIMER_TIME_LOW);
high = readl(priv->base + TIMER_TIME_HIGH);
time = ((u64)high << 32) | low;
return time;
}
static int goldfish_timer_of_to_plat(struct udevice *dev)
{
struct goldfish_timer_plat *plat = dev_get_plat(dev);
fdt_addr_t addr;
addr = dev_read_addr(dev);
if (addr != FDT_ADDR_T_NONE)
plat->reg = addr;
return 0;
}
static int goldfish_timer_probe(struct udevice *dev)
{
struct goldfish_timer_plat *plat = dev_get_plat(dev);
struct goldfish_timer_priv *priv = dev_get_priv(dev);
struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
if (!plat->reg)
return -EINVAL;
priv->base = map_sysmem(plat->reg, 0x20);
/* Goldfish RTC counts in nanoseconds, so the rate is 1GHz */
uc_priv->clock_rate = 1000000000;
return 0;
}
static const struct timer_ops goldfish_timer_ops = {
.get_count = goldfish_timer_get_count,
};
static const struct udevice_id goldfish_timer_ids[] = {
{ .compatible = "google,goldfish-rtc" },
{ }
};
U_BOOT_DRIVER(goldfish_timer) = {
.name = "goldfish_timer",
.id = UCLASS_TIMER,
.of_match = goldfish_timer_ids,
.of_to_plat = goldfish_timer_of_to_plat,
.plat_auto = sizeof(struct goldfish_timer_plat),
.ops = &goldfish_timer_ops,
.probe = goldfish_timer_probe,
.priv_auto = sizeof(struct goldfish_timer_priv),
};

View File

@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#ifndef __QEMU_M68K_H
#define __QEMU_M68K_H
/* Memory Configuration */
#define CFG_SYS_SDRAM_BASE 0x00000000
/*
* Initial Stack Pointer:
* Place the stack at 4MB offset to avoid overwriting U-Boot code/data.
*/
#define CFG_SYS_INIT_SP_ADDR (CFG_SYS_SDRAM_BASE + 0x400000)
#endif /* __QEMU_M68K_H */

15
include/goldfish_rtc.h Normal file
View File

@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#ifndef _GOLDFISH_RTC_H_
#define _GOLDFISH_RTC_H_
#include <linux/types.h>
struct goldfish_rtc_plat {
phys_addr_t reg;
};
#endif /* _GOLDFISH_RTC_H_ */

13
include/goldfish_timer.h Normal file
View File

@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#ifndef _GOLDFISH_TIMER_H_
#define _GOLDFISH_TIMER_H_
struct goldfish_timer_plat {
phys_addr_t reg;
};
#endif /* _GOLDFISH_TIMER_H_ */

18
include/goldfish_tty.h Normal file
View File

@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#ifndef _GOLDFISH_TTY_H_
#define _GOLDFISH_TTY_H_
#include <linux/types.h>
/* Platform data for the Goldfish TTY driver
* Used to pass hardware base address from Board to Driver
*/
struct goldfish_tty_plat {
phys_addr_t reg;
};
#endif /* _GOLDFISH_TTY_H_ */

13
include/qemu_virt_ctrl.h Normal file
View File

@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025, Kuan-Wei Chiu <visitorckw@gmail.com>
*/
#ifndef _QEMU_VIRT_CTRL_H_
#define _QEMU_VIRT_CTRL_H_
struct qemu_virt_ctrl_plat {
phys_addr_t reg;
};
#endif /* _QEMU_VIRT_CTRL_H_ */