1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
4  * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
5  */
6 
7 #include <dm.h>
8 #include <env.h>
9 #include <mapmem.h>
10 #include <qfw.h>
11 #include <stdlib.h>
12 #include <dm/uclass.h>
13 
qfw_get_dev(struct udevice ** devp)14 int qfw_get_dev(struct udevice **devp)
15 {
16 	return uclass_first_device_err(UCLASS_QFW, devp);
17 }
18 
qfw_online_cpus(struct udevice * dev)19 int qfw_online_cpus(struct udevice *dev)
20 {
21 	u16 nb_cpus;
22 
23 	qfw_read_entry(dev, FW_CFG_NB_CPUS, 2, &nb_cpus);
24 
25 	return le16_to_cpu(nb_cpus);
26 }
27 
qfw_read_firmware_list(struct udevice * dev)28 int qfw_read_firmware_list(struct udevice *dev)
29 {
30 	int i;
31 	u32 count;
32 	struct fw_file *file;
33 	struct list_head *entry;
34 
35 	struct qfw_dev *qdev = dev_get_uclass_priv(dev);
36 
37 	/* don't read it twice */
38 	if (!list_empty(&qdev->fw_list))
39 		return 0;
40 
41 	qfw_read_entry(dev, FW_CFG_FILE_DIR, 4, &count);
42 	if (!count)
43 		return 0;
44 
45 	count = be32_to_cpu(count);
46 	for (i = 0; i < count; i++) {
47 		file = malloc(sizeof(*file));
48 		if (!file) {
49 			printf("error: allocating resource\n");
50 			goto err;
51 		}
52 		qfw_read_entry(dev, FW_CFG_INVALID,
53 			       sizeof(struct fw_cfg_file), &file->cfg);
54 		file->addr = 0;
55 		list_add_tail(&file->list, &qdev->fw_list);
56 	}
57 
58 	return 0;
59 
60 err:
61 	list_for_each(entry, &qdev->fw_list) {
62 		file = list_entry(entry, struct fw_file, list);
63 		free(file);
64 	}
65 
66 	return -ENOMEM;
67 }
68 
qfw_find_file(struct udevice * dev,const char * name)69 struct fw_file *qfw_find_file(struct udevice *dev, const char *name)
70 {
71 	struct list_head *entry;
72 	struct fw_file *file;
73 
74 	struct qfw_dev *qdev = dev_get_uclass_priv(dev);
75 
76 	list_for_each(entry, &qdev->fw_list) {
77 		file = list_entry(entry, struct fw_file, list);
78 		if (!strcmp(file->cfg.name, name))
79 			return file;
80 	}
81 
82 	return NULL;
83 }
84 
qfw_file_iter_init(struct udevice * dev,struct fw_cfg_file_iter * iter)85 struct fw_file *qfw_file_iter_init(struct udevice *dev,
86 				   struct fw_cfg_file_iter *iter)
87 {
88 	struct qfw_dev *qdev = dev_get_uclass_priv(dev);
89 
90 	iter->entry = qdev->fw_list.next;
91 	iter->end = &qdev->fw_list;
92 	return list_entry((struct list_head *)iter->entry,
93 			  struct fw_file, list);
94 }
95 
qfw_file_iter_next(struct fw_cfg_file_iter * iter)96 struct fw_file *qfw_file_iter_next(struct fw_cfg_file_iter *iter)
97 {
98 	iter->entry = ((struct list_head *)iter->entry)->next;
99 	return list_entry((struct list_head *)iter->entry,
100 			  struct fw_file, list);
101 }
102 
qfw_file_iter_end(struct fw_cfg_file_iter * iter)103 bool qfw_file_iter_end(struct fw_cfg_file_iter *iter)
104 {
105 	return iter->entry == iter->end;
106 }
107 
qemu_fwcfg_setup_kernel(struct udevice * qfw_dev,ulong load_addr,ulong initrd_addr)108 int qemu_fwcfg_setup_kernel(struct udevice *qfw_dev, ulong load_addr,
109 			    ulong initrd_addr)
110 {
111 	char *data_addr;
112 	u32 setup_size, kernel_size, cmdline_size, initrd_size;
113 
114 	qfw_read_entry(qfw_dev, FW_CFG_SETUP_SIZE, 4, &setup_size);
115 	qfw_read_entry(qfw_dev, FW_CFG_KERNEL_SIZE, 4, &kernel_size);
116 
117 	if (!kernel_size) {
118 		printf("fatal: no kernel available\n");
119 		return -ENOENT;
120 	}
121 
122 	data_addr = map_sysmem(load_addr, 0);
123 	if (setup_size) {
124 		qfw_read_entry(qfw_dev, FW_CFG_SETUP_DATA,
125 			       le32_to_cpu(setup_size), data_addr);
126 		data_addr += le32_to_cpu(setup_size);
127 	}
128 
129 	qfw_read_entry(qfw_dev, FW_CFG_KERNEL_DATA,
130 		       le32_to_cpu(kernel_size), data_addr);
131 	data_addr += le32_to_cpu(kernel_size);
132 	env_set_hex("filesize", le32_to_cpu(kernel_size));
133 
134 	data_addr = map_sysmem(initrd_addr, 0);
135 	qfw_read_entry(qfw_dev, FW_CFG_INITRD_SIZE, 4, &initrd_size);
136 	if (!initrd_size) {
137 		printf("warning: no initrd available\n");
138 	} else {
139 		qfw_read_entry(qfw_dev, FW_CFG_INITRD_DATA,
140 			       le32_to_cpu(initrd_size), data_addr);
141 		data_addr += le32_to_cpu(initrd_size);
142 		env_set_hex("filesize", le32_to_cpu(initrd_size));
143 	}
144 
145 	qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_SIZE, 4, &cmdline_size);
146 	if (cmdline_size) {
147 		qfw_read_entry(qfw_dev, FW_CFG_CMDLINE_DATA,
148 			       le32_to_cpu(cmdline_size), data_addr);
149 		/*
150 		 * if kernel cmdline only contains '\0', (e.g. no -append
151 		 * when invoking qemu), do not update bootargs
152 		 */
153 		if (*data_addr) {
154 			if (env_set("bootargs", data_addr) < 0)
155 				printf("warning: unable to change bootargs\n");
156 		}
157 	}
158 
159 	printf("loading kernel to address %lx size %x", load_addr,
160 	       le32_to_cpu(kernel_size));
161 	if (initrd_size)
162 		printf(" initrd %lx size %x\n", initrd_addr,
163 		       le32_to_cpu(initrd_size));
164 	else
165 		printf("\n");
166 
167 	return 0;
168 }
169