1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Bootmethod for QEMU qfw
4 *
5 * Copyright 2023 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#define LOG_CATEGORY UCLASS_BOOTSTD
10
11#include <common.h>
12#include <command.h>
13#include <bootdev.h>
14#include <bootflow.h>
15#include <bootmeth.h>
16#include <env.h>
17#include <qfw.h>
18#include <dm.h>
19
20static int qfw_check(struct udevice *dev, struct bootflow_iter *iter)
21{
22	const struct udevice *media = dev_get_parent(iter->dev);
23	enum uclass_id id = device_get_uclass_id(media);
24
25	log_debug("media=%s\n", media->name);
26	if (id == UCLASS_QFW)
27		return 0;
28
29	return -ENOTSUPP;
30}
31
32static int qfw_read_bootflow(struct udevice *dev, struct bootflow *bflow)
33{
34	struct udevice *qfw_dev = dev_get_parent(bflow->dev);
35	ulong load, initrd;
36	int ret;
37
38	load = env_get_hex("kernel_addr_r", 0);
39	initrd = env_get_hex("ramdisk_addr_r", 0);
40	log_debug("setup kernel %s %lx %lx\n", qfw_dev->name, load, initrd);
41	bflow->name = strdup("qfw");
42	if (!bflow->name)
43		return log_msg_ret("name", -ENOMEM);
44
45	ret = qemu_fwcfg_setup_kernel(qfw_dev, load, initrd);
46	log_debug("setup kernel result %d\n", ret);
47	if (ret)
48		return log_msg_ret("cmd", -EIO);
49
50	bflow->state = BOOTFLOWST_READY;
51
52	return 0;
53}
54
55static int qfw_read_file(struct udevice *dev, struct bootflow *bflow,
56			 const char *file_path, ulong addr, ulong *sizep)
57{
58	return -ENOSYS;
59}
60
61static int qfw_boot(struct udevice *dev, struct bootflow *bflow)
62{
63	int ret;
64
65	ret = run_command("booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdtcontroladdr}",
66			  0);
67	if (ret) {
68		ret = run_command("bootz ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} "
69				  "${fdtcontroladdr}", 0);
70	}
71
72	return ret ? -EIO : 0;
73}
74
75static int qfw_bootmeth_bind(struct udevice *dev)
76{
77	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
78
79	plat->desc = "QEMU boot using firmware interface";
80
81	return 0;
82}
83
84static struct bootmeth_ops qfw_bootmeth_ops = {
85	.check		= qfw_check,
86	.read_bootflow	= qfw_read_bootflow,
87	.read_file	= qfw_read_file,
88	.boot		= qfw_boot,
89};
90
91static const struct udevice_id qfw_bootmeth_ids[] = {
92	{ .compatible = "u-boot,qfw-extlinux" },
93	{ }
94};
95
96U_BOOT_DRIVER(bootmeth_qfw) = {
97	.name		= "bootmeth_qfw",
98	.id		= UCLASS_BOOTMETH,
99	.of_match	= qfw_bootmeth_ids,
100	.ops		= &qfw_bootmeth_ops,
101	.bind		= qfw_bootmeth_bind,
102};
103