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#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 20DECLARE_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 */ 32static 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 */ 100static 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 */ 134static 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 */ 155ulong 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 224out: 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 244ulong 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 261static 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 280EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, evt_write_acpi_tables); 281#endif 282