- 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>master
parent
49d929bbc4
commit
dd6f3abbb8
@ -0,0 +1,243 @@ |
||||
/*
|
||||
* (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <command.h> |
||||
#include <errno.h> |
||||
#include <malloc.h> |
||||
#include <qemu_fw_cfg.h> |
||||
#include <asm/io.h> |
||||
#include <asm/tables.h> |
||||
#include <asm/e820.h> |
||||
#include <linux/list.h> |
||||
#include <memalign.h> |
||||
|
||||
/*
|
||||
* This function allocates memory for ACPI tables |
||||
* |
||||
* @entry : BIOS linker command entry which tells where to allocate memory |
||||
* (either high memory or low memory) |
||||
* @addr : The address that should be used for low memory allcation. If the |
||||
* memory allocation request is 'ZONE_HIGH' then this parameter will |
||||
* be ignored. |
||||
* @return: 0 on success, or negative value on failure |
||||
*/ |
||||
static int bios_linker_allocate(struct bios_linker_entry *entry, u32 *addr) |
||||
{ |
||||
uint32_t size, align; |
||||
struct fw_file *file; |
||||
unsigned long aligned_addr; |
||||
|
||||
align = le32_to_cpu(entry->alloc.align); |
||||
/* align must be power of 2 */ |
||||
if (align & (align - 1)) { |
||||
printf("error: wrong alignment %u\n", align); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
file = qemu_fwcfg_find_file(entry->alloc.file); |
||||
if (!file) { |
||||
printf("error: can't find file %s\n", entry->alloc.file); |
||||
return -ENOENT; |
||||
} |
||||
|
||||
size = be32_to_cpu(file->cfg.size); |
||||
|
||||
/*
|
||||
* ZONE_HIGH means we need to allocate from high memory, since |
||||
* malloc space is already at the end of RAM, so we directly use it. |
||||
* If allocation zone is ZONE_FSEG, then we use the 'addr' passed |
||||
* in which is low memory |
||||
*/ |
||||
if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { |
||||
aligned_addr = (unsigned long)memalign(align, size); |
||||
if (!aligned_addr) { |
||||
printf("error: allocating resource\n"); |
||||
return -ENOMEM; |
||||
} |
||||
} else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { |
||||
aligned_addr = ALIGN(*addr, align); |
||||
} else { |
||||
printf("error: invalid allocation zone\n"); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", |
||||
file->cfg.name, size, entry->alloc.zone, align, aligned_addr); |
||||
|
||||
qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), |
||||
size, (void *)aligned_addr); |
||||
file->addr = aligned_addr; |
||||
|
||||
/* adjust address for low memory allocation */ |
||||
if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) |
||||
*addr = (aligned_addr + size); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* This function patches ACPI tables previously loaded |
||||
* by bios_linker_allocate() |
||||
* |
||||
* @entry : BIOS linker command entry which tells how to patch |
||||
* ACPI tables |
||||
* @return: 0 on success, or negative value on failure |
||||
*/ |
||||
static int bios_linker_add_pointer(struct bios_linker_entry *entry) |
||||
{ |
||||
struct fw_file *dest, *src; |
||||
uint32_t offset = le32_to_cpu(entry->pointer.offset); |
||||
uint64_t pointer = 0; |
||||
|
||||
dest = qemu_fwcfg_find_file(entry->pointer.dest_file); |
||||
if (!dest || !dest->addr) |
||||
return -ENOENT; |
||||
src = qemu_fwcfg_find_file(entry->pointer.src_file); |
||||
if (!src || !src->addr) |
||||
return -ENOENT; |
||||
|
||||
debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", |
||||
dest->addr, src->addr, offset, entry->pointer.size, pointer); |
||||
|
||||
memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); |
||||
pointer = le64_to_cpu(pointer); |
||||
pointer += (unsigned long)src->addr; |
||||
pointer = cpu_to_le64(pointer); |
||||
memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* This function updates checksum fields of ACPI tables previously loaded |
||||
* by bios_linker_allocate() |
||||
* |
||||
* @entry : BIOS linker command entry which tells where to update ACPI table |
||||
* checksums |
||||
* @return: 0 on success, or negative value on failure |
||||
*/ |
||||
static int bios_linker_add_checksum(struct bios_linker_entry *entry) |
||||
{ |
||||
struct fw_file *file; |
||||
uint8_t *data, cksum = 0; |
||||
uint8_t *cksum_start; |
||||
|
||||
file = qemu_fwcfg_find_file(entry->cksum.file); |
||||
if (!file || !file->addr) |
||||
return -ENOENT; |
||||
|
||||
data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); |
||||
cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); |
||||
cksum = table_compute_checksum(cksum_start, |
||||
le32_to_cpu(entry->cksum.length)); |
||||
*data = cksum; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) |
||||
{ |
||||
entries[0].addr = 0; |
||||
entries[0].size = ISA_START_ADDRESS; |
||||
entries[0].type = E820_RAM; |
||||
|
||||
entries[1].addr = ISA_START_ADDRESS; |
||||
entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS; |
||||
entries[1].type = E820_RESERVED; |
||||
|
||||
/*
|
||||
* since we use memalign(malloc) to allocate high memory for |
||||
* storing ACPI tables, we need to reserve them in e820 tables, |
||||
* otherwise kernel will reclaim them and data will be corrupted |
||||
*/ |
||||
entries[2].addr = ISA_END_ADDRESS; |
||||
entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS; |
||||
entries[2].type = E820_RAM; |
||||
|
||||
/* for simplicity, reserve entire malloc space */ |
||||
entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN; |
||||
entries[3].size = TOTAL_MALLOC_LEN; |
||||
entries[3].type = E820_RESERVED; |
||||
|
||||
entries[4].addr = gd->relocaddr; |
||||
entries[4].size = gd->ram_size - gd->relocaddr; |
||||
entries[4].type = E820_RESERVED; |
||||
|
||||
entries[5].addr = CONFIG_PCIE_ECAM_BASE; |
||||
entries[5].size = CONFIG_PCIE_ECAM_SIZE; |
||||
entries[5].type = E820_RESERVED; |
||||
|
||||
return 6; |
||||
} |
||||
|
||||
/* This function loads and patches ACPI tables provided by QEMU */ |
||||
u32 write_acpi_tables(u32 addr) |
||||
{ |
||||
int i, ret = 0; |
||||
struct fw_file *file; |
||||
struct bios_linker_entry *table_loader; |
||||
struct bios_linker_entry *entry; |
||||
uint32_t size; |
||||
|
||||
/* make sure fw_list is loaded */ |
||||
ret = qemu_fwcfg_read_firmware_list(); |
||||
if (ret) { |
||||
printf("error: can't read firmware file list\n"); |
||||
return addr; |
||||
} |
||||
|
||||
file = qemu_fwcfg_find_file("etc/table-loader"); |
||||
if (!file) { |
||||
printf("error: can't find etc/table-loader\n"); |
||||
return addr; |
||||
} |
||||
|
||||
size = be32_to_cpu(file->cfg.size); |
||||
if ((size % sizeof(*entry)) != 0) { |
||||
printf("error: table-loader maybe corrupted\n"); |
||||
return addr; |
||||
} |
||||
|
||||
table_loader = malloc(size); |
||||
if (!table_loader) { |
||||
printf("error: no memory for table-loader\n"); |
||||
return addr; |
||||
} |
||||
|
||||
qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), |
||||
size, table_loader); |
||||
|
||||
for (i = 0; i < (size / sizeof(*entry)); i++) { |
||||
entry = table_loader + i; |
||||
switch (le32_to_cpu(entry->command)) { |
||||
case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: |
||||
ret = bios_linker_allocate(entry, &addr); |
||||
if (ret) |
||||
goto out; |
||||
break; |
||||
case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: |
||||
ret = bios_linker_add_pointer(entry); |
||||
if (ret) |
||||
goto out; |
||||
break; |
||||
case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: |
||||
ret = bios_linker_add_checksum(entry); |
||||
if (ret) |
||||
goto out; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
out: |
||||
if (ret) |
||||
qemu_fwcfg_free_files(); |
||||
|
||||
free(table_loader); |
||||
return addr; |
||||
} |
Loading…
Reference in new issue