blob: b17fa03bc897cc54121f895d9680bb8039117b8e [file] [log] [blame]
Tom Rinibcb3c8d2016-05-06 10:40:22 -04001/*
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 */
28static 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 */
90static 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 */
123static 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
142unsigned 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 */
178u32 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
237out:
Miao Yanb603eb12016-05-22 19:37:12 -0700238 if (ret) {
239 struct fw_cfg_file_iter iter;
240 for (file = qemu_fwcfg_file_iter_init(&iter);
241 !qemu_fwcfg_file_iter_end(&iter);
242 file = qemu_fwcfg_file_iter_next(&iter)) {
243 if (file->addr) {
244 free((void *)file->addr);
245 file->addr = 0;
246 }
247 }
248 }
Tom Rinibcb3c8d2016-05-06 10:40:22 -0400249
250 free(table_loader);
251 return addr;
252}