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