1 /* Dynamic linker/loader of ACPI tables 2 * 3 * Copyright (C) 2013 Red Hat Inc 4 * 5 * Author: Michael S. Tsirkin <mst@redhat.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu-common.h" 22 #include "hw/acpi/bios-linker-loader.h" 23 #include "hw/nvram/fw_cfg.h" 24 25 #include "qemu/bswap.h" 26 27 #define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH 28 29 struct BiosLinkerLoaderEntry { 30 uint32_t command; 31 union { 32 /* 33 * COMMAND_ALLOCATE - allocate a table from @alloc.file 34 * subject to @alloc.align alignment (must be power of 2) 35 * and @alloc.zone (can be HIGH or FSEG) requirements. 36 * 37 * Must appear exactly once for each file, and before 38 * this file is referenced by any other command. 39 */ 40 struct { 41 char file[BIOS_LINKER_LOADER_FILESZ]; 42 uint32_t align; 43 uint8_t zone; 44 } alloc; 45 46 /* 47 * COMMAND_ADD_POINTER - patch the table (originating from 48 * @dest_file) at @pointer.offset, by adding a pointer to the table 49 * originating from @src_file. 1,2,4 or 8 byte unsigned 50 * addition is used depending on @pointer.size. 51 */ 52 struct { 53 char dest_file[BIOS_LINKER_LOADER_FILESZ]; 54 char src_file[BIOS_LINKER_LOADER_FILESZ]; 55 uint32_t offset; 56 uint8_t size; 57 } pointer; 58 59 /* 60 * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by 61 * @cksum_start and @cksum_length fields, 62 * and then add the value at @cksum.offset. 63 * Checksum simply sums -X for each byte X in the range 64 * using 8-bit math. 65 */ 66 struct { 67 char file[BIOS_LINKER_LOADER_FILESZ]; 68 uint32_t offset; 69 uint32_t start; 70 uint32_t length; 71 } cksum; 72 73 /* padding */ 74 char pad[124]; 75 }; 76 } QEMU_PACKED; 77 typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry; 78 79 enum { 80 BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, 81 BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, 82 BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, 83 }; 84 85 enum { 86 BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1, 87 BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2, 88 }; 89 90 GArray *bios_linker_loader_init(void) 91 { 92 return g_array_new(false, true /* clear */, 1); 93 } 94 95 /* Free linker wrapper and return the linker array. */ 96 void *bios_linker_loader_cleanup(GArray *linker) 97 { 98 return g_array_free(linker, false); 99 } 100 101 void bios_linker_loader_alloc(GArray *linker, 102 const char *file, 103 uint32_t alloc_align, 104 bool alloc_fseg) 105 { 106 BiosLinkerLoaderEntry entry; 107 108 memset(&entry, 0, sizeof entry); 109 strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1); 110 entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE); 111 entry.alloc.align = cpu_to_le32(alloc_align); 112 entry.alloc.zone = cpu_to_le32(alloc_fseg ? 113 BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : 114 BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH); 115 116 /* Alloc entries must come first, so prepend them */ 117 g_array_prepend_vals(linker, &entry, sizeof entry); 118 } 119 120 void bios_linker_loader_add_checksum(GArray *linker, const char *file, 121 void *table, 122 void *start, unsigned size, 123 uint8_t *checksum) 124 { 125 BiosLinkerLoaderEntry entry; 126 127 memset(&entry, 0, sizeof entry); 128 strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1); 129 entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM); 130 entry.cksum.offset = cpu_to_le32(checksum - (uint8_t *)table); 131 entry.cksum.start = cpu_to_le32((uint8_t *)start - (uint8_t *)table); 132 entry.cksum.length = cpu_to_le32(size); 133 134 g_array_append_vals(linker, &entry, sizeof entry); 135 } 136 137 void bios_linker_loader_add_pointer(GArray *linker, 138 const char *dest_file, 139 const char *src_file, 140 GArray *table, void *pointer, 141 uint8_t pointer_size) 142 { 143 BiosLinkerLoaderEntry entry; 144 size_t offset = (gchar *)pointer - table->data; 145 146 memset(&entry, 0, sizeof entry); 147 strncpy(entry.pointer.dest_file, dest_file, 148 sizeof entry.pointer.dest_file - 1); 149 strncpy(entry.pointer.src_file, src_file, 150 sizeof entry.pointer.src_file - 1); 151 entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); 152 assert(table->len >= offset + pointer_size); 153 entry.pointer.offset = cpu_to_le32(offset); 154 entry.pointer.size = pointer_size; 155 assert(pointer_size == 1 || pointer_size == 2 || 156 pointer_size == 4 || pointer_size == 8); 157 158 g_array_append_vals(linker, &entry, sizeof entry); 159 } 160