Commit dd6f3abbb81d4d0f8883a523e26fd45833a6b0d3

Authored by Tom Rini
Committed by Bin Meng
1 parent 49d929bbc4

x86: qemu: Move qfw command over to cmd and add Kconfig entry

- Move the command portion of arch/x86/cpu/qemu/fw_cfg.c into
  cmd/qemu_fw_cfg.c
- Move arch/x86/include/asm/fw_cfg.h to include/qemu_fw_cfg.h
- Rename ACPI table portion to arch/x86/cpu/qemu/acpi_table.c

Signed-off-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

Showing 13 changed files with 763 additions and 732 deletions Side-by-side Diff

arch/x86/cpu/mp_init.c
... ... @@ -11,6 +11,7 @@
11 11 #include <dm.h>
12 12 #include <errno.h>
13 13 #include <malloc.h>
  14 +#include <qemu_fw_cfg.h>
14 15 #include <asm/atomic.h>
15 16 #include <asm/cpu.h>
16 17 #include <asm/interrupt.h>
... ... @@ -21,7 +22,6 @@
21 22 #include <asm/mtrr.h>
22 23 #include <asm/processor.h>
23 24 #include <asm/sipi.h>
24   -#include <asm/fw_cfg.h>
25 25 #include <dm/device-internal.h>
26 26 #include <dm/uclass-internal.h>
27 27 #include <dm/lists.h>
arch/x86/cpu/qemu/Makefile
... ... @@ -7,5 +7,6 @@
7 7 ifndef CONFIG_EFI_STUB
8 8 obj-y += car.o dram.o
9 9 endif
10   -obj-y += cpu.o fw_cfg.o qemu.o
  10 +obj-y += cpu.o qemu.o
  11 +obj-$(CONFIG_QEMU_ACPI_TABLE) += acpi_table.o
arch/x86/cpu/qemu/acpi_table.c
  1 +/*
  2 + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + */
  6 +
  7 +#include <common.h>
  8 +#include <command.h>
  9 +#include <errno.h>
  10 +#include <malloc.h>
  11 +#include <qemu_fw_cfg.h>
  12 +#include <asm/io.h>
  13 +#include <asm/tables.h>
  14 +#include <asm/e820.h>
  15 +#include <linux/list.h>
  16 +#include <memalign.h>
  17 +
  18 +/*
  19 + * This function allocates memory for ACPI tables
  20 + *
  21 + * @entry : BIOS linker command entry which tells where to allocate memory
  22 + * (either high memory or low memory)
  23 + * @addr : The address that should be used for low memory allcation. If the
  24 + * memory allocation request is 'ZONE_HIGH' then this parameter will
  25 + * be ignored.
  26 + * @return: 0 on success, or negative value on failure
  27 + */
  28 +static int bios_linker_allocate(struct bios_linker_entry *entry, u32 *addr)
  29 +{
  30 + uint32_t size, align;
  31 + struct fw_file *file;
  32 + unsigned long aligned_addr;
  33 +
  34 + align = le32_to_cpu(entry->alloc.align);
  35 + /* align must be power of 2 */
  36 + if (align & (align - 1)) {
  37 + printf("error: wrong alignment %u\n", align);
  38 + return -EINVAL;
  39 + }
  40 +
  41 + file = qemu_fwcfg_find_file(entry->alloc.file);
  42 + if (!file) {
  43 + printf("error: can't find file %s\n", entry->alloc.file);
  44 + return -ENOENT;
  45 + }
  46 +
  47 + size = be32_to_cpu(file->cfg.size);
  48 +
  49 + /*
  50 + * ZONE_HIGH means we need to allocate from high memory, since
  51 + * malloc space is already at the end of RAM, so we directly use it.
  52 + * If allocation zone is ZONE_FSEG, then we use the 'addr' passed
  53 + * in which is low memory
  54 + */
  55 + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
  56 + aligned_addr = (unsigned long)memalign(align, size);
  57 + if (!aligned_addr) {
  58 + printf("error: allocating resource\n");
  59 + return -ENOMEM;
  60 + }
  61 + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
  62 + aligned_addr = ALIGN(*addr, align);
  63 + } else {
  64 + printf("error: invalid allocation zone\n");
  65 + return -EINVAL;
  66 + }
  67 +
  68 + debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
  69 + file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
  70 +
  71 + qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
  72 + size, (void *)aligned_addr);
  73 + file->addr = aligned_addr;
  74 +
  75 + /* adjust address for low memory allocation */
  76 + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
  77 + *addr = (aligned_addr + size);
  78 +
  79 + return 0;
  80 +}
  81 +
  82 +/*
  83 + * This function patches ACPI tables previously loaded
  84 + * by bios_linker_allocate()
  85 + *
  86 + * @entry : BIOS linker command entry which tells how to patch
  87 + * ACPI tables
  88 + * @return: 0 on success, or negative value on failure
  89 + */
  90 +static int bios_linker_add_pointer(struct bios_linker_entry *entry)
  91 +{
  92 + struct fw_file *dest, *src;
  93 + uint32_t offset = le32_to_cpu(entry->pointer.offset);
  94 + uint64_t pointer = 0;
  95 +
  96 + dest = qemu_fwcfg_find_file(entry->pointer.dest_file);
  97 + if (!dest || !dest->addr)
  98 + return -ENOENT;
  99 + src = qemu_fwcfg_find_file(entry->pointer.src_file);
  100 + if (!src || !src->addr)
  101 + return -ENOENT;
  102 +
  103 + debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
  104 + dest->addr, src->addr, offset, entry->pointer.size, pointer);
  105 +
  106 + memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
  107 + pointer = le64_to_cpu(pointer);
  108 + pointer += (unsigned long)src->addr;
  109 + pointer = cpu_to_le64(pointer);
  110 + memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
  111 +
  112 + return 0;
  113 +}
  114 +
  115 +/*
  116 + * This function updates checksum fields of ACPI tables previously loaded
  117 + * by bios_linker_allocate()
  118 + *
  119 + * @entry : BIOS linker command entry which tells where to update ACPI table
  120 + * checksums
  121 + * @return: 0 on success, or negative value on failure
  122 + */
  123 +static int bios_linker_add_checksum(struct bios_linker_entry *entry)
  124 +{
  125 + struct fw_file *file;
  126 + uint8_t *data, cksum = 0;
  127 + uint8_t *cksum_start;
  128 +
  129 + file = qemu_fwcfg_find_file(entry->cksum.file);
  130 + if (!file || !file->addr)
  131 + return -ENOENT;
  132 +
  133 + data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
  134 + cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
  135 + cksum = table_compute_checksum(cksum_start,
  136 + le32_to_cpu(entry->cksum.length));
  137 + *data = cksum;
  138 +
  139 + return 0;
  140 +}
  141 +
  142 +unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
  143 +{
  144 + entries[0].addr = 0;
  145 + entries[0].size = ISA_START_ADDRESS;
  146 + entries[0].type = E820_RAM;
  147 +
  148 + entries[1].addr = ISA_START_ADDRESS;
  149 + entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
  150 + entries[1].type = E820_RESERVED;
  151 +
  152 + /*
  153 + * since we use memalign(malloc) to allocate high memory for
  154 + * storing ACPI tables, we need to reserve them in e820 tables,
  155 + * otherwise kernel will reclaim them and data will be corrupted
  156 + */
  157 + entries[2].addr = ISA_END_ADDRESS;
  158 + entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS;
  159 + entries[2].type = E820_RAM;
  160 +
  161 + /* for simplicity, reserve entire malloc space */
  162 + entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN;
  163 + entries[3].size = TOTAL_MALLOC_LEN;
  164 + entries[3].type = E820_RESERVED;
  165 +
  166 + entries[4].addr = gd->relocaddr;
  167 + entries[4].size = gd->ram_size - gd->relocaddr;
  168 + entries[4].type = E820_RESERVED;
  169 +
  170 + entries[5].addr = CONFIG_PCIE_ECAM_BASE;
  171 + entries[5].size = CONFIG_PCIE_ECAM_SIZE;
  172 + entries[5].type = E820_RESERVED;
  173 +
  174 + return 6;
  175 +}
  176 +
  177 +/* This function loads and patches ACPI tables provided by QEMU */
  178 +u32 write_acpi_tables(u32 addr)
  179 +{
  180 + int i, ret = 0;
  181 + struct fw_file *file;
  182 + struct bios_linker_entry *table_loader;
  183 + struct bios_linker_entry *entry;
  184 + uint32_t size;
  185 +
  186 + /* make sure fw_list is loaded */
  187 + ret = qemu_fwcfg_read_firmware_list();
  188 + if (ret) {
  189 + printf("error: can't read firmware file list\n");
  190 + return addr;
  191 + }
  192 +
  193 + file = qemu_fwcfg_find_file("etc/table-loader");
  194 + if (!file) {
  195 + printf("error: can't find etc/table-loader\n");
  196 + return addr;
  197 + }
  198 +
  199 + size = be32_to_cpu(file->cfg.size);
  200 + if ((size % sizeof(*entry)) != 0) {
  201 + printf("error: table-loader maybe corrupted\n");
  202 + return addr;
  203 + }
  204 +
  205 + table_loader = malloc(size);
  206 + if (!table_loader) {
  207 + printf("error: no memory for table-loader\n");
  208 + return addr;
  209 + }
  210 +
  211 + qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
  212 + size, table_loader);
  213 +
  214 + for (i = 0; i < (size / sizeof(*entry)); i++) {
  215 + entry = table_loader + i;
  216 + switch (le32_to_cpu(entry->command)) {
  217 + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
  218 + ret = bios_linker_allocate(entry, &addr);
  219 + if (ret)
  220 + goto out;
  221 + break;
  222 + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
  223 + ret = bios_linker_add_pointer(entry);
  224 + if (ret)
  225 + goto out;
  226 + break;
  227 + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
  228 + ret = bios_linker_add_checksum(entry);
  229 + if (ret)
  230 + goto out;
  231 + break;
  232 + default:
  233 + break;
  234 + }
  235 + }
  236 +
  237 +out:
  238 + if (ret)
  239 + qemu_fwcfg_free_files();
  240 +
  241 + free(table_loader);
  242 + return addr;
  243 +}
arch/x86/cpu/qemu/cpu.c
... ... @@ -8,8 +8,8 @@
8 8 #include <cpu.h>
9 9 #include <dm.h>
10 10 #include <errno.h>
  11 +#include <qemu_fw_cfg.h>
11 12 #include <asm/cpu.h>
12   -#include <asm/fw_cfg.h>
13 13  
14 14 DECLARE_GLOBAL_DATA_PTR;
15 15  
arch/x86/cpu/qemu/fw_cfg.c
1   -/*
2   - * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
3   - *
4   - * SPDX-License-Identifier: GPL-2.0+
5   - */
6   -
7   -#include <common.h>
8   -#include <command.h>
9   -#include <errno.h>
10   -#include <malloc.h>
11   -#include <asm/io.h>
12   -#include <asm/fw_cfg.h>
13   -#include <asm/tables.h>
14   -#include <asm/e820.h>
15   -#include <linux/list.h>
16   -#include <memalign.h>
17   -
18   -static bool fwcfg_present;
19   -static bool fwcfg_dma_present;
20   -
21   -static LIST_HEAD(fw_list);
22   -
23   -/* Read configuration item using fw_cfg PIO interface */
24   -static void qemu_fwcfg_read_entry_pio(uint16_t entry,
25   - uint32_t size, void *address)
26   -{
27   - uint32_t i = 0;
28   - uint8_t *data = address;
29   -
30   - /*
31   - * writting FW_CFG_INVALID will cause read operation to resume at
32   - * last offset, otherwise read will start at offset 0
33   - */
34   - if (entry != FW_CFG_INVALID)
35   - outw(entry, FW_CONTROL_PORT);
36   - while (size--)
37   - data[i++] = inb(FW_DATA_PORT);
38   -}
39   -
40   -/* Read configuration item using fw_cfg DMA interface */
41   -static void qemu_fwcfg_read_entry_dma(uint16_t entry,
42   - uint32_t size, void *address)
43   -{
44   - struct fw_cfg_dma_access dma;
45   -
46   - dma.length = cpu_to_be32(size);
47   - dma.address = cpu_to_be64((uintptr_t)address);
48   - dma.control = cpu_to_be32(FW_CFG_DMA_READ);
49   -
50   - /*
51   - * writting FW_CFG_INVALID will cause read operation to resume at
52   - * last offset, otherwise read will start at offset 0
53   - */
54   - if (entry != FW_CFG_INVALID)
55   - dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16));
56   -
57   - barrier();
58   -
59   - debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n",
60   - address, size, be32_to_cpu(dma.control));
61   -
62   - outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH);
63   -
64   - while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_ERROR)
65   - __asm__ __volatile__ ("pause");
66   -}
67   -
68   -static bool qemu_fwcfg_present(void)
69   -{
70   - uint32_t qemu;
71   -
72   - qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu);
73   - return be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE;
74   -}
75   -
76   -static bool qemu_fwcfg_dma_present(void)
77   -{
78   - uint8_t dma_enabled;
79   -
80   - qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled);
81   - if (dma_enabled & FW_CFG_DMA_ENABLED)
82   - return true;
83   -
84   - return false;
85   -}
86   -
87   -static void qemu_fwcfg_read_entry(uint16_t entry,
88   - uint32_t length, void *address)
89   -{
90   - if (fwcfg_dma_present)
91   - qemu_fwcfg_read_entry_dma(entry, length, address);
92   - else
93   - qemu_fwcfg_read_entry_pio(entry, length, address);
94   -}
95   -
96   -int qemu_fwcfg_online_cpus(void)
97   -{
98   - uint16_t nb_cpus;
99   -
100   - if (!fwcfg_present)
101   - return -ENODEV;
102   -
103   - qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus);
104   -
105   - return le16_to_cpu(nb_cpus);
106   -}
107   -
108   -/*
109   - * This function prepares kernel for zboot. It loads kernel data
110   - * to 'load_addr', initrd to 'initrd_addr' and kernel command
111   - * line using qemu fw_cfg interface.
112   - */
113   -static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr)
114   -{
115   - char *data_addr;
116   - uint32_t setup_size, kernel_size, cmdline_size, initrd_size;
117   -
118   - qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size);
119   - qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size);
120   -
121   - if (setup_size == 0 || kernel_size == 0) {
122   - printf("warning: no kernel available\n");
123   - return -1;
124   - }
125   -
126   - data_addr = load_addr;
127   - qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA,
128   - le32_to_cpu(setup_size), data_addr);
129   - data_addr += le32_to_cpu(setup_size);
130   -
131   - qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA,
132   - le32_to_cpu(kernel_size), data_addr);
133   - data_addr += le32_to_cpu(kernel_size);
134   -
135   - data_addr = initrd_addr;
136   - qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size);
137   - if (initrd_size == 0) {
138   - printf("warning: no initrd available\n");
139   - } else {
140   - qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA,
141   - le32_to_cpu(initrd_size), data_addr);
142   - data_addr += le32_to_cpu(initrd_size);
143   - }
144   -
145   - qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size);
146   - if (cmdline_size) {
147   - qemu_fwcfg_read_entry(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 != '\0') {
154   - if (setenv("bootargs", data_addr) < 0)
155   - printf("warning: unable to change bootargs\n");
156   - }
157   - }
158   -
159   - printf("loading kernel to address %p size %x", load_addr,
160   - le32_to_cpu(kernel_size));
161   - if (initrd_size)
162   - printf(" initrd %p size %x\n",
163   - initrd_addr,
164   - le32_to_cpu(initrd_size));
165   - else
166   - printf("\n");
167   -
168   - return 0;
169   -}
170   -
171   -static int qemu_fwcfg_read_firmware_list(void)
172   -{
173   - int i;
174   - uint32_t count;
175   - struct fw_file *file;
176   - struct list_head *entry;
177   -
178   - /* don't read it twice */
179   - if (!list_empty(&fw_list))
180   - return 0;
181   -
182   - qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count);
183   - if (!count)
184   - return 0;
185   -
186   - count = be32_to_cpu(count);
187   - for (i = 0; i < count; i++) {
188   - file = malloc(sizeof(*file));
189   - if (!file) {
190   - printf("error: allocating resource\n");
191   - goto err;
192   - }
193   - qemu_fwcfg_read_entry(FW_CFG_INVALID,
194   - sizeof(struct fw_cfg_file), &file->cfg);
195   - file->addr = 0;
196   - list_add_tail(&file->list, &fw_list);
197   - }
198   -
199   - return 0;
200   -
201   -err:
202   - list_for_each(entry, &fw_list) {
203   - file = list_entry(entry, struct fw_file, list);
204   - free(file);
205   - }
206   -
207   - return -ENOMEM;
208   -}
209   -
210   -#ifdef CONFIG_QEMU_ACPI_TABLE
211   -static struct fw_file *qemu_fwcfg_find_file(const char *name)
212   -{
213   - struct list_head *entry;
214   - struct fw_file *file;
215   -
216   - list_for_each(entry, &fw_list) {
217   - file = list_entry(entry, struct fw_file, list);
218   - if (!strcmp(file->cfg.name, name))
219   - return file;
220   - }
221   -
222   - return NULL;
223   -}
224   -
225   -/*
226   - * This function allocates memory for ACPI tables
227   - *
228   - * @entry : BIOS linker command entry which tells where to allocate memory
229   - * (either high memory or low memory)
230   - * @addr : The address that should be used for low memory allcation. If the
231   - * memory allocation request is 'ZONE_HIGH' then this parameter will
232   - * be ignored.
233   - * @return: 0 on success, or negative value on failure
234   - */
235   -static int bios_linker_allocate(struct bios_linker_entry *entry, u32 *addr)
236   -{
237   - uint32_t size, align;
238   - struct fw_file *file;
239   - unsigned long aligned_addr;
240   -
241   - align = le32_to_cpu(entry->alloc.align);
242   - /* align must be power of 2 */
243   - if (align & (align - 1)) {
244   - printf("error: wrong alignment %u\n", align);
245   - return -EINVAL;
246   - }
247   -
248   - file = qemu_fwcfg_find_file(entry->alloc.file);
249   - if (!file) {
250   - printf("error: can't find file %s\n", entry->alloc.file);
251   - return -ENOENT;
252   - }
253   -
254   - size = be32_to_cpu(file->cfg.size);
255   -
256   - /*
257   - * ZONE_HIGH means we need to allocate from high memory, since
258   - * malloc space is already at the end of RAM, so we directly use it.
259   - * If allocation zone is ZONE_FSEG, then we use the 'addr' passed
260   - * in which is low memory
261   - */
262   - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
263   - aligned_addr = (unsigned long)memalign(align, size);
264   - if (!aligned_addr) {
265   - printf("error: allocating resource\n");
266   - return -ENOMEM;
267   - }
268   - } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
269   - aligned_addr = ALIGN(*addr, align);
270   - } else {
271   - printf("error: invalid allocation zone\n");
272   - return -EINVAL;
273   - }
274   -
275   - debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
276   - file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
277   -
278   - qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
279   - size, (void *)aligned_addr);
280   - file->addr = aligned_addr;
281   -
282   - /* adjust address for low memory allocation */
283   - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
284   - *addr = (aligned_addr + size);
285   -
286   - return 0;
287   -}
288   -
289   -/*
290   - * This function patches ACPI tables previously loaded
291   - * by bios_linker_allocate()
292   - *
293   - * @entry : BIOS linker command entry which tells how to patch
294   - * ACPI tables
295   - * @return: 0 on success, or negative value on failure
296   - */
297   -static int bios_linker_add_pointer(struct bios_linker_entry *entry)
298   -{
299   - struct fw_file *dest, *src;
300   - uint32_t offset = le32_to_cpu(entry->pointer.offset);
301   - uint64_t pointer = 0;
302   -
303   - dest = qemu_fwcfg_find_file(entry->pointer.dest_file);
304   - if (!dest || !dest->addr)
305   - return -ENOENT;
306   - src = qemu_fwcfg_find_file(entry->pointer.src_file);
307   - if (!src || !src->addr)
308   - return -ENOENT;
309   -
310   - debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
311   - dest->addr, src->addr, offset, entry->pointer.size, pointer);
312   -
313   - memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
314   - pointer = le64_to_cpu(pointer);
315   - pointer += (unsigned long)src->addr;
316   - pointer = cpu_to_le64(pointer);
317   - memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
318   -
319   - return 0;
320   -}
321   -
322   -/*
323   - * This function updates checksum fields of ACPI tables previously loaded
324   - * by bios_linker_allocate()
325   - *
326   - * @entry : BIOS linker command entry which tells where to update ACPI table
327   - * checksums
328   - * @return: 0 on success, or negative value on failure
329   - */
330   -static int bios_linker_add_checksum(struct bios_linker_entry *entry)
331   -{
332   - struct fw_file *file;
333   - uint8_t *data, cksum = 0;
334   - uint8_t *cksum_start;
335   -
336   - file = qemu_fwcfg_find_file(entry->cksum.file);
337   - if (!file || !file->addr)
338   - return -ENOENT;
339   -
340   - data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
341   - cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
342   - cksum = table_compute_checksum(cksum_start,
343   - le32_to_cpu(entry->cksum.length));
344   - *data = cksum;
345   -
346   - return 0;
347   -}
348   -
349   -unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
350   -{
351   - entries[0].addr = 0;
352   - entries[0].size = ISA_START_ADDRESS;
353   - entries[0].type = E820_RAM;
354   -
355   - entries[1].addr = ISA_START_ADDRESS;
356   - entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
357   - entries[1].type = E820_RESERVED;
358   -
359   - /*
360   - * since we use memalign(malloc) to allocate high memory for
361   - * storing ACPI tables, we need to reserve them in e820 tables,
362   - * otherwise kernel will reclaim them and data will be corrupted
363   - */
364   - entries[2].addr = ISA_END_ADDRESS;
365   - entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS;
366   - entries[2].type = E820_RAM;
367   -
368   - /* for simplicity, reserve entire malloc space */
369   - entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN;
370   - entries[3].size = TOTAL_MALLOC_LEN;
371   - entries[3].type = E820_RESERVED;
372   -
373   - entries[4].addr = gd->relocaddr;
374   - entries[4].size = gd->ram_size - gd->relocaddr;
375   - entries[4].type = E820_RESERVED;
376   -
377   - entries[5].addr = CONFIG_PCIE_ECAM_BASE;
378   - entries[5].size = CONFIG_PCIE_ECAM_SIZE;
379   - entries[5].type = E820_RESERVED;
380   -
381   - return 6;
382   -}
383   -
384   -/* This function loads and patches ACPI tables provided by QEMU */
385   -u32 write_acpi_tables(u32 addr)
386   -{
387   - int i, ret = 0;
388   - struct fw_file *file;
389   - struct bios_linker_entry *table_loader;
390   - struct bios_linker_entry *entry;
391   - uint32_t size;
392   - struct list_head *list;
393   -
394   - /* make sure fw_list is loaded */
395   - ret = qemu_fwcfg_read_firmware_list();
396   - if (ret) {
397   - printf("error: can't read firmware file list\n");
398   - return addr;
399   - }
400   -
401   - file = qemu_fwcfg_find_file("etc/table-loader");
402   - if (!file) {
403   - printf("error: can't find etc/table-loader\n");
404   - return addr;
405   - }
406   -
407   - size = be32_to_cpu(file->cfg.size);
408   - if ((size % sizeof(*entry)) != 0) {
409   - printf("error: table-loader maybe corrupted\n");
410   - return addr;
411   - }
412   -
413   - table_loader = malloc(size);
414   - if (!table_loader) {
415   - printf("error: no memory for table-loader\n");
416   - return addr;
417   - }
418   -
419   - qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select),
420   - size, table_loader);
421   -
422   - for (i = 0; i < (size / sizeof(*entry)); i++) {
423   - entry = table_loader + i;
424   - switch (le32_to_cpu(entry->command)) {
425   - case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
426   - ret = bios_linker_allocate(entry, &addr);
427   - if (ret)
428   - goto out;
429   - break;
430   - case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
431   - ret = bios_linker_add_pointer(entry);
432   - if (ret)
433   - goto out;
434   - break;
435   - case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
436   - ret = bios_linker_add_checksum(entry);
437   - if (ret)
438   - goto out;
439   - break;
440   - default:
441   - break;
442   - }
443   - }
444   -
445   -out:
446   - if (ret) {
447   - list_for_each(list, &fw_list) {
448   - file = list_entry(list, struct fw_file, list);
449   - if (file->addr)
450   - free((void *)file->addr);
451   - }
452   - }
453   -
454   - free(table_loader);
455   - return addr;
456   -}
457   -#endif
458   -
459   -static int qemu_fwcfg_list_firmware(void)
460   -{
461   - int ret;
462   - struct list_head *entry;
463   - struct fw_file *file;
464   -
465   - /* make sure fw_list is loaded */
466   - ret = qemu_fwcfg_read_firmware_list();
467   - if (ret)
468   - return ret;
469   -
470   - list_for_each(entry, &fw_list) {
471   - file = list_entry(entry, struct fw_file, list);
472   - printf("%-56s\n", file->cfg.name);
473   - }
474   -
475   - return 0;
476   -}
477   -
478   -void qemu_fwcfg_init(void)
479   -{
480   - fwcfg_present = qemu_fwcfg_present();
481   - if (fwcfg_present)
482   - fwcfg_dma_present = qemu_fwcfg_dma_present();
483   -}
484   -
485   -static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag,
486   - int argc, char * const argv[])
487   -{
488   - if (qemu_fwcfg_list_firmware() < 0)
489   - return CMD_RET_FAILURE;
490   -
491   - return 0;
492   -}
493   -
494   -static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag,
495   - int argc, char * const argv[])
496   -{
497   - int ret = qemu_fwcfg_online_cpus();
498   - if (ret < 0) {
499   - printf("QEMU fw_cfg interface not found\n");
500   - return CMD_RET_FAILURE;
501   - }
502   -
503   - printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus());
504   -
505   - return 0;
506   -}
507   -
508   -static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag,
509   - int argc, char * const argv[])
510   -{
511   - char *env;
512   - void *load_addr;
513   - void *initrd_addr;
514   -
515   - env = getenv("loadaddr");
516   - load_addr = env ?
517   - (void *)simple_strtoul(env, NULL, 16) :
518   - (void *)CONFIG_LOADADDR;
519   -
520   - env = getenv("ramdiskaddr");
521   - initrd_addr = env ?
522   - (void *)simple_strtoul(env, NULL, 16) :
523   - (void *)CONFIG_RAMDISK_ADDR;
524   -
525   - if (argc == 2) {
526   - load_addr = (void *)simple_strtoul(argv[0], NULL, 16);
527   - initrd_addr = (void *)simple_strtoul(argv[1], NULL, 16);
528   - } else if (argc == 1) {
529   - load_addr = (void *)simple_strtoul(argv[0], NULL, 16);
530   - }
531   -
532   - return qemu_fwcfg_setup_kernel(load_addr, initrd_addr);
533   -}
534   -
535   -static cmd_tbl_t fwcfg_commands[] = {
536   - U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""),
537   - U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""),
538   - U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""),
539   -};
540   -
541   -static int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
542   -{
543   - int ret;
544   - cmd_tbl_t *fwcfg_cmd;
545   -
546   - if (!fwcfg_present) {
547   - printf("QEMU fw_cfg interface not found\n");
548   - return CMD_RET_USAGE;
549   - }
550   -
551   - fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands,
552   - ARRAY_SIZE(fwcfg_commands));
553   - argc -= 2;
554   - argv += 2;
555   - if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs)
556   - return CMD_RET_USAGE;
557   -
558   - ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv);
559   -
560   - return cmd_process_error(fwcfg_cmd, ret);
561   -}
562   -
563   -U_BOOT_CMD(
564   - qfw, 4, 1, do_qemu_fw,
565   - "QEMU firmware interface",
566   - "<command>\n"
567   - " - list : print firmware(s) currently loaded\n"
568   - " - cpus : print online cpu number\n"
569   - " - load <kernel addr> <initrd addr> : load kernel and initrd (if any), and setup for zboot\n"
570   -)
arch/x86/cpu/qemu/qemu.c
... ... @@ -6,12 +6,12 @@
6 6  
7 7 #include <common.h>
8 8 #include <pci.h>
  9 +#include <qemu_fw_cfg.h>
9 10 #include <asm/irq.h>
10 11 #include <asm/post.h>
11 12 #include <asm/processor.h>
12 13 #include <asm/arch/device.h>
13 14 #include <asm/arch/qemu.h>
14   -#include <asm/fw_cfg.h>
15 15  
16 16 static bool i440fx;
17 17  
arch/x86/include/asm/fw_cfg.h
1   -/*
2   - * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
3   - *
4   - * SPDX-License-Identifier: GPL-2.0+
5   - */
6   -
7   -#ifndef __FW_CFG__
8   -#define __FW_CFG__
9   -
10   -#define FW_CONTROL_PORT 0x510
11   -#define FW_DATA_PORT 0x511
12   -#define FW_DMA_PORT_LOW 0x514
13   -#define FW_DMA_PORT_HIGH 0x518
14   -
15   -#include <linux/list.h>
16   -
17   -enum qemu_fwcfg_items {
18   - FW_CFG_SIGNATURE = 0x00,
19   - FW_CFG_ID = 0x01,
20   - FW_CFG_UUID = 0x02,
21   - FW_CFG_RAM_SIZE = 0x03,
22   - FW_CFG_NOGRAPHIC = 0x04,
23   - FW_CFG_NB_CPUS = 0x05,
24   - FW_CFG_MACHINE_ID = 0x06,
25   - FW_CFG_KERNEL_ADDR = 0x07,
26   - FW_CFG_KERNEL_SIZE = 0x08,
27   - FW_CFG_KERNEL_CMDLINE = 0x09,
28   - FW_CFG_INITRD_ADDR = 0x0a,
29   - FW_CFG_INITRD_SIZE = 0x0b,
30   - FW_CFG_BOOT_DEVICE = 0x0c,
31   - FW_CFG_NUMA = 0x0d,
32   - FW_CFG_BOOT_MENU = 0x0e,
33   - FW_CFG_MAX_CPUS = 0x0f,
34   - FW_CFG_KERNEL_ENTRY = 0x10,
35   - FW_CFG_KERNEL_DATA = 0x11,
36   - FW_CFG_INITRD_DATA = 0x12,
37   - FW_CFG_CMDLINE_ADDR = 0x13,
38   - FW_CFG_CMDLINE_SIZE = 0x14,
39   - FW_CFG_CMDLINE_DATA = 0x15,
40   - FW_CFG_SETUP_ADDR = 0x16,
41   - FW_CFG_SETUP_SIZE = 0x17,
42   - FW_CFG_SETUP_DATA = 0x18,
43   - FW_CFG_FILE_DIR = 0x19,
44   - FW_CFG_FILE_FIRST = 0x20,
45   - FW_CFG_WRITE_CHANNEL = 0x4000,
46   - FW_CFG_ARCH_LOCAL = 0x8000,
47   - FW_CFG_INVALID = 0xffff,
48   -};
49   -
50   -enum {
51   - BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1,
52   - BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2,
53   - BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
54   -};
55   -
56   -enum {
57   - BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1,
58   - BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
59   -};
60   -
61   -#define FW_CFG_FILE_SLOTS 0x10
62   -#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS)
63   -#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
64   -
65   -#define FW_CFG_MAX_FILE_PATH 56
66   -#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
67   -
68   -#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U')
69   -
70   -#define FW_CFG_DMA_ERROR (1 << 0)
71   -#define FW_CFG_DMA_READ (1 << 1)
72   -#define FW_CFG_DMA_SKIP (1 << 2)
73   -#define FW_CFG_DMA_SELECT (1 << 3)
74   -
75   -#define FW_CFG_DMA_ENABLED (1 << 1)
76   -
77   -struct fw_cfg_file {
78   - __be32 size;
79   - __be16 select;
80   - __be16 reserved;
81   - char name[FW_CFG_MAX_FILE_PATH];
82   -};
83   -
84   -struct fw_file {
85   - struct fw_cfg_file cfg; /* firmware file information */
86   - unsigned long addr; /* firmware file in-memory address */
87   - struct list_head list; /* list node to link to fw_list */
88   -};
89   -
90   -struct fw_cfg_dma_access {
91   - __be32 control;
92   - __be32 length;
93   - __be64 address;
94   -};
95   -
96   -struct bios_linker_entry {
97   - __le32 command;
98   - union {
99   - /*
100   - * COMMAND_ALLOCATE - allocate a table from @alloc.file
101   - * subject to @alloc.align alignment (must be power of 2)
102   - * and @alloc.zone (can be HIGH or FSEG) requirements.
103   - *
104   - * Must appear exactly once for each file, and before
105   - * this file is referenced by any other command.
106   - */
107   - struct {
108   - char file[BIOS_LINKER_LOADER_FILESZ];
109   - __le32 align;
110   - uint8_t zone;
111   - } alloc;
112   -
113   - /*
114   - * COMMAND_ADD_POINTER - patch the table (originating from
115   - * @dest_file) at @pointer.offset, by adding a pointer to the
116   - * table originating from @src_file. 1,2,4 or 8 byte unsigned
117   - * addition is used depending on @pointer.size.
118   - */
119   - struct {
120   - char dest_file[BIOS_LINKER_LOADER_FILESZ];
121   - char src_file[BIOS_LINKER_LOADER_FILESZ];
122   - __le32 offset;
123   - uint8_t size;
124   - } pointer;
125   -
126   - /*
127   - * COMMAND_ADD_CHECKSUM - calculate checksum of the range
128   - * specified by @cksum_start and @cksum_length fields,
129   - * and then add the value at @cksum.offset.
130   - * Checksum simply sums -X for each byte X in the range
131   - * using 8-bit math.
132   - */
133   - struct {
134   - char file[BIOS_LINKER_LOADER_FILESZ];
135   - __le32 offset;
136   - __le32 start;
137   - __le32 length;
138   - } cksum;
139   -
140   - /* padding */
141   - char pad[124];
142   - };
143   -} __packed;
144   -
145   -/**
146   - * Initialize QEMU fw_cfg interface
147   - */
148   -void qemu_fwcfg_init(void);
149   -
150   -/**
151   - * Get system cpu number
152   - *
153   - * @return: cpu number in system
154   - */
155   -int qemu_fwcfg_online_cpus(void);
156   -
157   -#endif
arch/x86/lib/acpi_table.c
... ... @@ -303,7 +303,7 @@
303 303  
304 304 /*
305 305 * QEMU's version of write_acpi_tables is defined in
306   - * arch/x86/cpu/qemu/fw_cfg.c
  306 + * arch/x86/cpu/qemu/acpi_table.c
307 307 */
308 308 u32 write_acpi_tables(u32 start)
309 309 {
... ... @@ -593,6 +593,13 @@
593 593 sound init - set up sound system
594 594 sound play - play a sound
595 595  
  596 +config CMD_QEMU_FW_CFG
  597 + bool "qfw"
  598 + depends on X86
  599 + help
  600 + This provides access to the QEMU firmware interface. The main
  601 + feature is to allow easy loading of files passed to qemu-system
  602 + via -kernel / -initrd
596 603 endmenu
597 604  
598 605 config CMD_BOOTSTAGE
... ... @@ -105,6 +105,7 @@
105 105 obj-y += pcmcia.o
106 106 obj-$(CONFIG_CMD_PORTIO) += portio.o
107 107 obj-$(CONFIG_CMD_PXE) += pxe.o
  108 +obj-$(CONFIG_CMD_QEMU_FW_CFG) += qemu_fw_cfg.o
108 109 obj-$(CONFIG_CMD_READ) += read.o
109 110 obj-$(CONFIG_CMD_REGINFO) += reginfo.o
110 111 obj-$(CONFIG_CMD_REISER) += reiser.o
  1 +/*
  2 + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + */
  6 +
  7 +#include <common.h>
  8 +#include <command.h>
  9 +#include <errno.h>
  10 +#include <malloc.h>
  11 +#include <qemu_fw_cfg.h>
  12 +#include <asm/io.h>
  13 +#include <linux/list.h>
  14 +
  15 +static bool fwcfg_present;
  16 +static bool fwcfg_dma_present;
  17 +
  18 +static LIST_HEAD(fw_list);
  19 +
  20 +/* Read configuration item using fw_cfg PIO interface */
  21 +static void qemu_fwcfg_read_entry_pio(uint16_t entry,
  22 + uint32_t size, void *address)
  23 +{
  24 + uint32_t i = 0;
  25 + uint8_t *data = address;
  26 +
  27 + /*
  28 + * writting FW_CFG_INVALID will cause read operation to resume at
  29 + * last offset, otherwise read will start at offset 0
  30 + */
  31 + if (entry != FW_CFG_INVALID)
  32 + outw(entry, FW_CONTROL_PORT);
  33 + while (size--)
  34 + data[i++] = inb(FW_DATA_PORT);
  35 +}
  36 +
  37 +/* Read configuration item using fw_cfg DMA interface */
  38 +static void qemu_fwcfg_read_entry_dma(uint16_t entry,
  39 + uint32_t size, void *address)
  40 +{
  41 + struct fw_cfg_dma_access dma;
  42 +
  43 + dma.length = cpu_to_be32(size);
  44 + dma.address = cpu_to_be64((uintptr_t)address);
  45 + dma.control = cpu_to_be32(FW_CFG_DMA_READ);
  46 +
  47 + /*
  48 + * writting FW_CFG_INVALID will cause read operation to resume at
  49 + * last offset, otherwise read will start at offset 0
  50 + */
  51 + if (entry != FW_CFG_INVALID)
  52 + dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16));
  53 +
  54 + barrier();
  55 +
  56 + debug("qemu_fwcfg_dma_read_entry: addr %p, length %u control 0x%x\n",
  57 + address, size, be32_to_cpu(dma.control));
  58 +
  59 + outl(cpu_to_be32((uint32_t)&dma), FW_DMA_PORT_HIGH);
  60 +
  61 + while (be32_to_cpu(dma.control) & ~FW_CFG_DMA_ERROR)
  62 + __asm__ __volatile__ ("pause");
  63 +}
  64 +
  65 +static bool qemu_fwcfg_present(void)
  66 +{
  67 + uint32_t qemu;
  68 +
  69 + qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu);
  70 + return be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE;
  71 +}
  72 +
  73 +static bool qemu_fwcfg_dma_present(void)
  74 +{
  75 + uint8_t dma_enabled;
  76 +
  77 + qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled);
  78 + if (dma_enabled & FW_CFG_DMA_ENABLED)
  79 + return true;
  80 +
  81 + return false;
  82 +}
  83 +
  84 +void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address)
  85 +{
  86 + if (fwcfg_dma_present)
  87 + qemu_fwcfg_read_entry_dma(entry, length, address);
  88 + else
  89 + qemu_fwcfg_read_entry_pio(entry, length, address);
  90 +}
  91 +
  92 +int qemu_fwcfg_online_cpus(void)
  93 +{
  94 + uint16_t nb_cpus;
  95 +
  96 + if (!fwcfg_present)
  97 + return -ENODEV;
  98 +
  99 + qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus);
  100 +
  101 + return le16_to_cpu(nb_cpus);
  102 +}
  103 +
  104 +/*
  105 + * This function prepares kernel for zboot. It loads kernel data
  106 + * to 'load_addr', initrd to 'initrd_addr' and kernel command
  107 + * line using qemu fw_cfg interface.
  108 + */
  109 +static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr)
  110 +{
  111 + char *data_addr;
  112 + uint32_t setup_size, kernel_size, cmdline_size, initrd_size;
  113 +
  114 + qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size);
  115 + qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size);
  116 +
  117 + if (setup_size == 0 || kernel_size == 0) {
  118 + printf("warning: no kernel available\n");
  119 + return -1;
  120 + }
  121 +
  122 + data_addr = load_addr;
  123 + qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA,
  124 + le32_to_cpu(setup_size), data_addr);
  125 + data_addr += le32_to_cpu(setup_size);
  126 +
  127 + qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA,
  128 + le32_to_cpu(kernel_size), data_addr);
  129 + data_addr += le32_to_cpu(kernel_size);
  130 +
  131 + data_addr = initrd_addr;
  132 + qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size);
  133 + if (initrd_size == 0) {
  134 + printf("warning: no initrd available\n");
  135 + } else {
  136 + qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA,
  137 + le32_to_cpu(initrd_size), data_addr);
  138 + data_addr += le32_to_cpu(initrd_size);
  139 + }
  140 +
  141 + qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size);
  142 + if (cmdline_size) {
  143 + qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA,
  144 + le32_to_cpu(cmdline_size), data_addr);
  145 + /*
  146 + * if kernel cmdline only contains '\0', (e.g. no -append
  147 + * when invoking qemu), do not update bootargs
  148 + */
  149 + if (*data_addr != '\0') {
  150 + if (setenv("bootargs", data_addr) < 0)
  151 + printf("warning: unable to change bootargs\n");
  152 + }
  153 + }
  154 +
  155 + printf("loading kernel to address %p size %x", load_addr,
  156 + le32_to_cpu(kernel_size));
  157 + if (initrd_size)
  158 + printf(" initrd %p size %x\n",
  159 + initrd_addr,
  160 + le32_to_cpu(initrd_size));
  161 + else
  162 + printf("\n");
  163 +
  164 + return 0;
  165 +}
  166 +
  167 +int qemu_fwcfg_read_firmware_list(void)
  168 +{
  169 + int i;
  170 + uint32_t count;
  171 + struct fw_file *file;
  172 + struct list_head *entry;
  173 +
  174 + /* don't read it twice */
  175 + if (!list_empty(&fw_list))
  176 + return 0;
  177 +
  178 + qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count);
  179 + if (!count)
  180 + return 0;
  181 +
  182 + count = be32_to_cpu(count);
  183 + for (i = 0; i < count; i++) {
  184 + file = malloc(sizeof(*file));
  185 + if (!file) {
  186 + printf("error: allocating resource\n");
  187 + goto err;
  188 + }
  189 + qemu_fwcfg_read_entry(FW_CFG_INVALID,
  190 + sizeof(struct fw_cfg_file), &file->cfg);
  191 + file->addr = 0;
  192 + list_add_tail(&file->list, &fw_list);
  193 + }
  194 +
  195 + return 0;
  196 +
  197 +err:
  198 + list_for_each(entry, &fw_list) {
  199 + file = list_entry(entry, struct fw_file, list);
  200 + free(file);
  201 + }
  202 +
  203 + return -ENOMEM;
  204 +}
  205 +
  206 +struct fw_file *qemu_fwcfg_find_file(const char *name)
  207 +{
  208 + struct list_head *entry;
  209 + struct fw_file *file;
  210 +
  211 + list_for_each(entry, &fw_list) {
  212 + file = list_entry(entry, struct fw_file, list);
  213 + if (!strcmp(file->cfg.name, name))
  214 + return file;
  215 + }
  216 +
  217 + return NULL;
  218 +}
  219 +
  220 +void qemu_fwcfg_free_files(void)
  221 +{
  222 + struct fw_file *file;
  223 + struct list_head *list;
  224 +
  225 + list_for_each(list, &fw_list) {
  226 + file = list_entry(list, struct fw_file, list);
  227 + if (file->addr)
  228 + free((void *)file->addr);
  229 + }
  230 +}
  231 +
  232 +static int qemu_fwcfg_list_firmware(void)
  233 +{
  234 + int ret;
  235 + struct list_head *entry;
  236 + struct fw_file *file;
  237 +
  238 + /* make sure fw_list is loaded */
  239 + ret = qemu_fwcfg_read_firmware_list();
  240 + if (ret)
  241 + return ret;
  242 +
  243 + list_for_each(entry, &fw_list) {
  244 + file = list_entry(entry, struct fw_file, list);
  245 + printf("%-56s\n", file->cfg.name);
  246 + }
  247 +
  248 + return 0;
  249 +}
  250 +
  251 +void qemu_fwcfg_init(void)
  252 +{
  253 + fwcfg_present = qemu_fwcfg_present();
  254 + if (fwcfg_present)
  255 + fwcfg_dma_present = qemu_fwcfg_dma_present();
  256 +}
  257 +
  258 +static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag,
  259 + int argc, char * const argv[])
  260 +{
  261 + if (qemu_fwcfg_list_firmware() < 0)
  262 + return CMD_RET_FAILURE;
  263 +
  264 + return 0;
  265 +}
  266 +
  267 +static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag,
  268 + int argc, char * const argv[])
  269 +{
  270 + int ret = qemu_fwcfg_online_cpus();
  271 + if (ret < 0) {
  272 + printf("QEMU fw_cfg interface not found\n");
  273 + return CMD_RET_FAILURE;
  274 + }
  275 +
  276 + printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus());
  277 +
  278 + return 0;
  279 +}
  280 +
  281 +static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag,
  282 + int argc, char * const argv[])
  283 +{
  284 + char *env;
  285 + void *load_addr;
  286 + void *initrd_addr;
  287 +
  288 + env = getenv("loadaddr");
  289 + load_addr = env ?
  290 + (void *)simple_strtoul(env, NULL, 16) :
  291 + (void *)CONFIG_LOADADDR;
  292 +
  293 + env = getenv("ramdiskaddr");
  294 + initrd_addr = env ?
  295 + (void *)simple_strtoul(env, NULL, 16) :
  296 + (void *)CONFIG_RAMDISK_ADDR;
  297 +
  298 + if (argc == 2) {
  299 + load_addr = (void *)simple_strtoul(argv[0], NULL, 16);
  300 + initrd_addr = (void *)simple_strtoul(argv[1], NULL, 16);
  301 + } else if (argc == 1) {
  302 + load_addr = (void *)simple_strtoul(argv[0], NULL, 16);
  303 + }
  304 +
  305 + return qemu_fwcfg_setup_kernel(load_addr, initrd_addr);
  306 +}
  307 +
  308 +static cmd_tbl_t fwcfg_commands[] = {
  309 + U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""),
  310 + U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""),
  311 + U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""),
  312 +};
  313 +
  314 +static int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  315 +{
  316 + int ret;
  317 + cmd_tbl_t *fwcfg_cmd;
  318 +
  319 + if (!fwcfg_present) {
  320 + printf("QEMU fw_cfg interface not found\n");
  321 + return CMD_RET_USAGE;
  322 + }
  323 +
  324 + fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands,
  325 + ARRAY_SIZE(fwcfg_commands));
  326 + argc -= 2;
  327 + argv += 2;
  328 + if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs)
  329 + return CMD_RET_USAGE;
  330 +
  331 + ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv);
  332 +
  333 + return cmd_process_error(fwcfg_cmd, ret);
  334 +}
  335 +
  336 +U_BOOT_CMD(
  337 + qfw, 4, 1, do_qemu_fw,
  338 + "QEMU firmware interface",
  339 + "<command>\n"
  340 + " - list : print firmware(s) currently loaded\n"
  341 + " - cpus : print online cpu number\n"
  342 + " - load <kernel addr> <initrd addr> : load kernel and initrd (if any), and setup for zboot\n"
  343 +)
configs/qemu-x86_defconfig
... ... @@ -20,6 +20,7 @@
20 20 # CONFIG_CMD_NFS is not set
21 21 CONFIG_CMD_PING=y
22 22 CONFIG_CMD_TIME=y
  23 +CONFIG_CMD_QEMU_FW_CFG=y
23 24 CONFIG_CMD_BOOTSTAGE=y
24 25 CONFIG_CMD_EXT2=y
25 26 CONFIG_CMD_EXT4=y
include/qemu_fw_cfg.h
  1 +/*
  2 + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
  3 + *
  4 + * SPDX-License-Identifier: GPL-2.0+
  5 + */
  6 +
  7 +#ifndef __FW_CFG__
  8 +#define __FW_CFG__
  9 +
  10 +#define FW_CONTROL_PORT 0x510
  11 +#define FW_DATA_PORT 0x511
  12 +#define FW_DMA_PORT_LOW 0x514
  13 +#define FW_DMA_PORT_HIGH 0x518
  14 +
  15 +#include <linux/list.h>
  16 +
  17 +enum qemu_fwcfg_items {
  18 + FW_CFG_SIGNATURE = 0x00,
  19 + FW_CFG_ID = 0x01,
  20 + FW_CFG_UUID = 0x02,
  21 + FW_CFG_RAM_SIZE = 0x03,
  22 + FW_CFG_NOGRAPHIC = 0x04,
  23 + FW_CFG_NB_CPUS = 0x05,
  24 + FW_CFG_MACHINE_ID = 0x06,
  25 + FW_CFG_KERNEL_ADDR = 0x07,
  26 + FW_CFG_KERNEL_SIZE = 0x08,
  27 + FW_CFG_KERNEL_CMDLINE = 0x09,
  28 + FW_CFG_INITRD_ADDR = 0x0a,
  29 + FW_CFG_INITRD_SIZE = 0x0b,
  30 + FW_CFG_BOOT_DEVICE = 0x0c,
  31 + FW_CFG_NUMA = 0x0d,
  32 + FW_CFG_BOOT_MENU = 0x0e,
  33 + FW_CFG_MAX_CPUS = 0x0f,
  34 + FW_CFG_KERNEL_ENTRY = 0x10,
  35 + FW_CFG_KERNEL_DATA = 0x11,
  36 + FW_CFG_INITRD_DATA = 0x12,
  37 + FW_CFG_CMDLINE_ADDR = 0x13,
  38 + FW_CFG_CMDLINE_SIZE = 0x14,
  39 + FW_CFG_CMDLINE_DATA = 0x15,
  40 + FW_CFG_SETUP_ADDR = 0x16,
  41 + FW_CFG_SETUP_SIZE = 0x17,
  42 + FW_CFG_SETUP_DATA = 0x18,
  43 + FW_CFG_FILE_DIR = 0x19,
  44 + FW_CFG_FILE_FIRST = 0x20,
  45 + FW_CFG_WRITE_CHANNEL = 0x4000,
  46 + FW_CFG_ARCH_LOCAL = 0x8000,
  47 + FW_CFG_INVALID = 0xffff,
  48 +};
  49 +
  50 +enum {
  51 + BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1,
  52 + BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2,
  53 + BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
  54 +};
  55 +
  56 +enum {
  57 + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1,
  58 + BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
  59 +};
  60 +
  61 +#define FW_CFG_FILE_SLOTS 0x10
  62 +#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS)
  63 +#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
  64 +
  65 +#define FW_CFG_MAX_FILE_PATH 56
  66 +#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
  67 +
  68 +#define QEMU_FW_CFG_SIGNATURE (('Q' << 24) | ('E' << 16) | ('M' << 8) | 'U')
  69 +
  70 +#define FW_CFG_DMA_ERROR (1 << 0)
  71 +#define FW_CFG_DMA_READ (1 << 1)
  72 +#define FW_CFG_DMA_SKIP (1 << 2)
  73 +#define FW_CFG_DMA_SELECT (1 << 3)
  74 +
  75 +#define FW_CFG_DMA_ENABLED (1 << 1)
  76 +
  77 +struct fw_cfg_file {
  78 + __be32 size;
  79 + __be16 select;
  80 + __be16 reserved;
  81 + char name[FW_CFG_MAX_FILE_PATH];
  82 +};
  83 +
  84 +struct fw_file {
  85 + struct fw_cfg_file cfg; /* firmware file information */
  86 + unsigned long addr; /* firmware file in-memory address */
  87 + struct list_head list; /* list node to link to fw_list */
  88 +};
  89 +
  90 +struct fw_cfg_dma_access {
  91 + __be32 control;
  92 + __be32 length;
  93 + __be64 address;
  94 +};
  95 +
  96 +struct bios_linker_entry {
  97 + __le32 command;
  98 + union {
  99 + /*
  100 + * COMMAND_ALLOCATE - allocate a table from @alloc.file
  101 + * subject to @alloc.align alignment (must be power of 2)
  102 + * and @alloc.zone (can be HIGH or FSEG) requirements.
  103 + *
  104 + * Must appear exactly once for each file, and before
  105 + * this file is referenced by any other command.
  106 + */
  107 + struct {
  108 + char file[BIOS_LINKER_LOADER_FILESZ];
  109 + __le32 align;
  110 + uint8_t zone;
  111 + } alloc;
  112 +
  113 + /*
  114 + * COMMAND_ADD_POINTER - patch the table (originating from
  115 + * @dest_file) at @pointer.offset, by adding a pointer to the
  116 + * table originating from @src_file. 1,2,4 or 8 byte unsigned
  117 + * addition is used depending on @pointer.size.
  118 + */
  119 + struct {
  120 + char dest_file[BIOS_LINKER_LOADER_FILESZ];
  121 + char src_file[BIOS_LINKER_LOADER_FILESZ];
  122 + __le32 offset;
  123 + uint8_t size;
  124 + } pointer;
  125 +
  126 + /*
  127 + * COMMAND_ADD_CHECKSUM - calculate checksum of the range
  128 + * specified by @cksum_start and @cksum_length fields,
  129 + * and then add the value at @cksum.offset.
  130 + * Checksum simply sums -X for each byte X in the range
  131 + * using 8-bit math.
  132 + */
  133 + struct {
  134 + char file[BIOS_LINKER_LOADER_FILESZ];
  135 + __le32 offset;
  136 + __le32 start;
  137 + __le32 length;
  138 + } cksum;
  139 +
  140 + /* padding */
  141 + char pad[124];
  142 + };
  143 +} __packed;
  144 +
  145 +/**
  146 + * Initialize QEMU fw_cfg interface
  147 + */
  148 +void qemu_fwcfg_init(void);
  149 +
  150 +void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address);
  151 +int qemu_fwcfg_read_firmware_list(void);
  152 +struct fw_file *qemu_fwcfg_find_file(const char *name);
  153 +void qemu_fwcfg_free_files(void);
  154 +
  155 +/**
  156 + * Get system cpu number
  157 + *
  158 + * @return: cpu number in system
  159 + */
  160 +int qemu_fwcfg_online_cpus(void);
  161 +
  162 +#endif