]>
Commit | Line | Data |
---|---|---|
5b0b43e0 AC |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2015 Miao Yan <[email protected]> | |
4 | * (C) Copyright 2021 Asherah Connor <[email protected]> | |
5 | */ | |
6 | ||
7 | #include <dm.h> | |
00fa989e SG |
8 | #include <env.h> |
9 | #include <mapmem.h> | |
5b0b43e0 AC |
10 | #include <qfw.h> |
11 | #include <stdlib.h> | |
00fa989e | 12 | #include <dm/uclass.h> |
5b0b43e0 AC |
13 | |
14 | int qfw_get_dev(struct udevice **devp) | |
15 | { | |
16 | return uclass_first_device_err(UCLASS_QFW, devp); | |
17 | } | |
18 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
103 | bool qfw_file_iter_end(struct fw_cfg_file_iter *iter) | |
104 | { | |
105 | return iter->entry == iter->end; | |
106 | } | |
00fa989e SG |
107 | |
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 | } |