]> Git Repo - J-u-boot.git/blob - drivers/misc/qfw_acpi.c
net: mediatek: fix gmac2 usability for mt7629
[J-u-boot.git] / drivers / misc / qfw_acpi.c
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 #define LOG_CATEGORY UCLASS_QFW
8
9 #include <acpi/acpi_table.h>
10 #include <errno.h>
11 #include <malloc.h>
12 #include <mapmem.h>
13 #include <qfw.h>
14 #include <tables_csum.h>
15 #include <stdio.h>
16 #include <linux/sizes.h>
17 #include <asm/byteorder.h>
18 #include <asm/global_data.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 /*
23  * This function allocates memory for ACPI tables
24  *
25  * @entry : BIOS linker command entry which tells where to allocate memory
26  *          (either high memory or low memory)
27  * @addr  : The address that should be used for low memory allcation. If the
28  *          memory allocation request is 'ZONE_HIGH' then this parameter will
29  *          be ignored.
30  * @return: 0 on success, or negative value on failure
31  */
32 static int bios_linker_allocate(struct udevice *dev,
33                                 struct bios_linker_entry *entry, ulong *addr)
34 {
35         uint32_t size, align;
36         struct fw_file *file;
37         unsigned long aligned_addr;
38
39         align = le32_to_cpu(entry->alloc.align);
40         /* align must be power of 2 */
41         if (align & (align - 1)) {
42                 printf("error: wrong alignment %u\n", align);
43                 return -EINVAL;
44         }
45
46         file = qfw_find_file(dev, entry->alloc.file);
47         if (!file) {
48                 printf("error: can't find file %s\n", entry->alloc.file);
49                 return -ENOENT;
50         }
51
52         size = be32_to_cpu(file->cfg.size);
53
54         /*
55          * ZONE_HIGH means we need to allocate from high memory, since
56          * malloc space is already at the end of RAM, so we directly use it.
57          * If allocation zone is ZONE_FSEG, then we use the 'addr' passed
58          * in which is low memory
59          */
60         if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) {
61                 aligned_addr = (unsigned long)memalign(align, size);
62                 if (!aligned_addr) {
63                         printf("error: allocating resource\n");
64                         return -ENOMEM;
65                 }
66                 if (aligned_addr < gd->arch.table_start_high)
67                         gd->arch.table_start_high = aligned_addr;
68                 if (aligned_addr + size > gd->arch.table_end_high)
69                         gd->arch.table_end_high = aligned_addr + size;
70
71         } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) {
72                 aligned_addr = ALIGN(*addr, align);
73         } else {
74                 printf("error: invalid allocation zone\n");
75                 return -EINVAL;
76         }
77
78         debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n",
79               file->cfg.name, size, entry->alloc.zone, align, aligned_addr);
80
81         qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size,
82                        (void *)aligned_addr);
83         file->addr = aligned_addr;
84
85         /* adjust address for low memory allocation */
86         if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG)
87                 *addr = (aligned_addr + size);
88
89         return 0;
90 }
91
92 /*
93  * This function patches ACPI tables previously loaded
94  * by bios_linker_allocate()
95  *
96  * @entry : BIOS linker command entry which tells how to patch
97  *          ACPI tables
98  * @return: 0 on success, or negative value on failure
99  */
100 static int bios_linker_add_pointer(struct udevice *dev,
101                                    struct bios_linker_entry *entry)
102 {
103         struct fw_file *dest, *src;
104         uint32_t offset = le32_to_cpu(entry->pointer.offset);
105         uint64_t pointer = 0;
106
107         dest = qfw_find_file(dev, entry->pointer.dest_file);
108         if (!dest || !dest->addr)
109                 return -ENOENT;
110         src = qfw_find_file(dev, entry->pointer.src_file);
111         if (!src || !src->addr)
112                 return -ENOENT;
113
114         debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n",
115               dest->addr, src->addr, offset, entry->pointer.size, pointer);
116
117         memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size);
118         pointer = le64_to_cpu(pointer);
119         pointer += (unsigned long)src->addr;
120         pointer = cpu_to_le64(pointer);
121         memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size);
122
123         return 0;
124 }
125
126 /*
127  * This function updates checksum fields of ACPI tables previously loaded
128  * by bios_linker_allocate()
129  *
130  * @entry : BIOS linker command entry which tells where to update ACPI table
131  *          checksums
132  * @return: 0 on success, or negative value on failure
133  */
134 static int bios_linker_add_checksum(struct udevice *dev,
135                                     struct bios_linker_entry *entry)
136 {
137         struct fw_file *file;
138         uint8_t *data, cksum = 0;
139         uint8_t *cksum_start;
140
141         file = qfw_find_file(dev, entry->cksum.file);
142         if (!file || !file->addr)
143                 return -ENOENT;
144
145         data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset));
146         cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start));
147         cksum = table_compute_checksum(cksum_start,
148                                        le32_to_cpu(entry->cksum.length));
149         *data = cksum;
150
151         return 0;
152 }
153
154 /* This function loads and patches ACPI tables provided by QEMU */
155 ulong write_acpi_tables(ulong addr)
156 {
157         int i, ret;
158         struct fw_file *file;
159         struct bios_linker_entry *table_loader;
160         struct bios_linker_entry *entry;
161         uint32_t size;
162         struct udevice *dev;
163
164         ret = qfw_get_dev(&dev);
165         if (ret) {
166                 printf("error: no qfw\n");
167                 return addr;
168         }
169
170         /* make sure fw_list is loaded */
171         ret = qfw_read_firmware_list(dev);
172         if (ret) {
173                 printf("error: can't read firmware file list\n");
174                 return addr;
175         }
176
177         file = qfw_find_file(dev, "etc/table-loader");
178         if (!file) {
179                 printf("error: can't find etc/table-loader\n");
180                 return addr;
181         }
182
183         size = be32_to_cpu(file->cfg.size);
184         if ((size % sizeof(*entry)) != 0) {
185                 printf("error: table-loader maybe corrupted\n");
186                 return addr;
187         }
188
189         table_loader = malloc(size);
190         if (!table_loader) {
191                 printf("error: no memory for table-loader\n");
192                 return addr;
193         }
194
195         /* QFW always puts tables at high addresses */
196         gd->arch.table_start_high = (ulong)table_loader;
197         gd->arch.table_end_high = (ulong)table_loader;
198
199         qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader);
200
201         for (i = 0; i < (size / sizeof(*entry)); i++) {
202                 entry = table_loader + i;
203                 switch (le32_to_cpu(entry->command)) {
204                 case BIOS_LINKER_LOADER_COMMAND_ALLOCATE:
205                         ret = bios_linker_allocate(dev, entry, &addr);
206                         if (ret)
207                                 goto out;
208                         break;
209                 case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER:
210                         ret = bios_linker_add_pointer(dev, entry);
211                         if (ret)
212                                 goto out;
213                         break;
214                 case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM:
215                         ret = bios_linker_add_checksum(dev, entry);
216                         if (ret)
217                                 goto out;
218                         break;
219                 default:
220                         break;
221                 }
222         }
223
224 out:
225         if (ret) {
226                 struct fw_cfg_file_iter iter;
227                 for (file = qfw_file_iter_init(dev, &iter);
228                      !qfw_file_iter_end(&iter);
229                      file = qfw_file_iter_next(&iter)) {
230                         if (file->addr) {
231                                 free((void *)file->addr);
232                                 file->addr = 0;
233                         }
234                 }
235         }
236
237         free(table_loader);
238
239         gd_set_acpi_start(acpi_get_rsdp_addr());
240
241         return addr;
242 }
243
244 ulong acpi_get_rsdp_addr(void)
245 {
246         int ret;
247         struct fw_file *file;
248         struct udevice *dev;
249
250         ret = qfw_get_dev(&dev);
251         if (ret) {
252                 printf("error: no qfw\n");
253                 return 0;
254         }
255
256         file = qfw_find_file(dev, "etc/acpi/rsdp");
257         return file->addr;
258 }
259
260 #ifndef CONFIG_X86
261 static int evt_write_acpi_tables(void)
262 {
263         ulong addr, end;
264         void *ptr;
265
266         /* Reserve 64K for ACPI tables, aligned to a 4K boundary */
267         ptr = memalign(SZ_4K, SZ_64K);
268         if (!ptr)
269                 return -ENOMEM;
270         addr = map_to_sysmem(ptr);
271
272         /* Generate ACPI tables */
273         end = write_acpi_tables(addr);
274         gd->arch.table_start = addr;
275         gd->arch.table_end = addr;
276
277         return 0;
278 }
279
280 EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, evt_write_acpi_tables);
281 #endif
This page took 0.041629 seconds and 4 git commands to generate.