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/osdep.h" 22 #include "qemu-common.h" 23 #include "hw/acpi/bios-linker-loader.h" 24 #include "hw/nvram/fw_cfg.h" 25 26 #include "qemu/bswap.h" 27 28 #define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH 29 30 struct BiosLinkerLoaderEntry { 31 uint32_t command; 32 union { 33 /* 34 * COMMAND_ALLOCATE - allocate a table from @alloc.file 35 * subject to @alloc.align alignment (must be power of 2) 36 * and @alloc.zone (can be HIGH or FSEG) requirements. 37 * 38 * Must appear exactly once for each file, and before 39 * this file is referenced by any other command. 40 */ 41 struct { 42 char file[BIOS_LINKER_LOADER_FILESZ]; 43 uint32_t align; 44 uint8_t zone; 45 } alloc; 46 47 /* 48 * COMMAND_ADD_POINTER - patch the table (originating from 49 * @dest_file) at @pointer.offset, by adding a pointer to the table 50 * originating from @src_file. 1,2,4 or 8 byte unsigned 51 * addition is used depending on @pointer.size. 52 */ 53 struct { 54 char dest_file[BIOS_LINKER_LOADER_FILESZ]; 55 char src_file[BIOS_LINKER_LOADER_FILESZ]; 56 uint32_t offset; 57 uint8_t size; 58 } pointer; 59 60 /* 61 * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by 62 * @cksum_start and @cksum_length fields, 63 * and then add the value at @cksum.offset. 64 * Checksum simply sums -X for each byte X in the range 65 * using 8-bit math. 66 */ 67 struct { 68 char file[BIOS_LINKER_LOADER_FILESZ]; 69 uint32_t offset; 70 uint32_t start; 71 uint32_t length; 72 } cksum; 73 74 /* padding */ 75 char pad[124]; 76 }; 77 } QEMU_PACKED; 78 typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry; 79 80 enum { 81 BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, 82 BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, 83 BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, 84 }; 85 86 enum { 87 BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1, 88 BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2, 89 }; 90 91 GArray *bios_linker_loader_init(void) 92 { 93 return g_array_new(false, true /* clear */, 1); 94 } 95 96 /* Free linker wrapper and return the linker array. */ 97 void *bios_linker_loader_cleanup(GArray *linker) 98 { 99 return g_array_free(linker, false); 100 } 101 102 void bios_linker_loader_alloc(GArray *linker, 103 const char *file, 104 uint32_t alloc_align, 105 bool alloc_fseg) 106 { 107 BiosLinkerLoaderEntry entry; 108 109 memset(&entry, 0, sizeof entry); 110 strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1); 111 entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE); 112 entry.alloc.align = cpu_to_le32(alloc_align); 113 entry.alloc.zone = cpu_to_le32(alloc_fseg ? 114 BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : 115 BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH); 116 117 /* Alloc entries must come first, so prepend them */ 118 g_array_prepend_vals(linker, &entry, sizeof entry); 119 } 120 121 void bios_linker_loader_add_checksum(GArray *linker, const char *file, 122 void *table, 123 void *start, unsigned size, 124 uint8_t *checksum) 125 { 126 BiosLinkerLoaderEntry entry; 127 128 memset(&entry, 0, sizeof entry); 129 strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1); 130 entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM); 131 entry.cksum.offset = cpu_to_le32(checksum - (uint8_t *)table); 132 entry.cksum.start = cpu_to_le32((uint8_t *)start - (uint8_t *)table); 133 entry.cksum.length = cpu_to_le32(size); 134 135 g_array_append_vals(linker, &entry, sizeof entry); 136 } 137 138 void bios_linker_loader_add_pointer(GArray *linker, 139 const char *dest_file, 140 const char *src_file, 141 GArray *table, void *pointer, 142 uint8_t pointer_size) 143 { 144 BiosLinkerLoaderEntry entry; 145 size_t offset = (gchar *)pointer - table->data; 146 147 memset(&entry, 0, sizeof entry); 148 strncpy(entry.pointer.dest_file, dest_file, 149 sizeof entry.pointer.dest_file - 1); 150 strncpy(entry.pointer.src_file, src_file, 151 sizeof entry.pointer.src_file - 1); 152 entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); 153 assert(table->len >= offset + pointer_size); 154 entry.pointer.offset = cpu_to_le32(offset); 155 entry.pointer.size = pointer_size; 156 assert(pointer_size == 1 || pointer_size == 2 || 157 pointer_size == 4 || pointer_size == 8); 158 159 g_array_append_vals(linker, &entry, sizeof entry); 160 } 161