109852232SMichael S. Tsirkin /* Dynamic linker/loader of ACPI tables
209852232SMichael S. Tsirkin *
309852232SMichael S. Tsirkin * Copyright (C) 2013 Red Hat Inc
409852232SMichael S. Tsirkin *
509852232SMichael S. Tsirkin * Author: Michael S. Tsirkin <mst@redhat.com>
609852232SMichael S. Tsirkin *
709852232SMichael S. Tsirkin * This program is free software; you can redistribute it and/or modify
809852232SMichael S. Tsirkin * it under the terms of the GNU General Public License as published by
909852232SMichael S. Tsirkin * the Free Software Foundation; either version 2 of the License, or
1009852232SMichael S. Tsirkin * (at your option) any later version.
1109852232SMichael S. Tsirkin
1209852232SMichael S. Tsirkin * This program is distributed in the hope that it will be useful,
1309852232SMichael S. Tsirkin * but WITHOUT ANY WARRANTY; without even the implied warranty of
1409852232SMichael S. Tsirkin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1509852232SMichael S. Tsirkin * GNU General Public License for more details.
1609852232SMichael S. Tsirkin
1709852232SMichael S. Tsirkin * You should have received a copy of the GNU General Public License along
1809852232SMichael S. Tsirkin * with this program; if not, see <http://www.gnu.org/licenses/>.
1909852232SMichael S. Tsirkin */
2009852232SMichael S. Tsirkin
21b6a0aa05SPeter Maydell #include "qemu/osdep.h"
2209852232SMichael S. Tsirkin #include "hw/acpi/bios-linker-loader.h"
2309852232SMichael S. Tsirkin #include "hw/nvram/fw_cfg.h"
2409852232SMichael S. Tsirkin
2509852232SMichael S. Tsirkin #include "qemu/bswap.h"
2609852232SMichael S. Tsirkin
27b54ca0c3SMichael S. Tsirkin /*
28b54ca0c3SMichael S. Tsirkin * Linker/loader is a paravirtualized interface that passes commands to guest.
29b54ca0c3SMichael S. Tsirkin * The commands can be used to request guest to
30b54ca0c3SMichael S. Tsirkin * - allocate memory chunks and initialize them from QEMU FW CFG files
31b54ca0c3SMichael S. Tsirkin * - link allocated chunks by storing pointer to one chunk into another
32b54ca0c3SMichael S. Tsirkin * - calculate ACPI checksum of part of the chunk and store into same chunk
33b54ca0c3SMichael S. Tsirkin */
3409852232SMichael S. Tsirkin #define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
3509852232SMichael S. Tsirkin
3609852232SMichael S. Tsirkin struct BiosLinkerLoaderEntry {
3709852232SMichael S. Tsirkin uint32_t command;
3809852232SMichael S. Tsirkin union {
3909852232SMichael S. Tsirkin /*
4009852232SMichael S. Tsirkin * COMMAND_ALLOCATE - allocate a table from @alloc.file
4109852232SMichael S. Tsirkin * subject to @alloc.align alignment (must be power of 2)
4209852232SMichael S. Tsirkin * and @alloc.zone (can be HIGH or FSEG) requirements.
4309852232SMichael S. Tsirkin *
4409852232SMichael S. Tsirkin * Must appear exactly once for each file, and before
4509852232SMichael S. Tsirkin * this file is referenced by any other command.
4609852232SMichael S. Tsirkin */
4709852232SMichael S. Tsirkin struct {
4809852232SMichael S. Tsirkin char file[BIOS_LINKER_LOADER_FILESZ];
4909852232SMichael S. Tsirkin uint32_t align;
5009852232SMichael S. Tsirkin uint8_t zone;
5109852232SMichael S. Tsirkin } alloc;
5209852232SMichael S. Tsirkin
5309852232SMichael S. Tsirkin /*
5409852232SMichael S. Tsirkin * COMMAND_ADD_POINTER - patch the table (originating from
5509852232SMichael S. Tsirkin * @dest_file) at @pointer.offset, by adding a pointer to the table
5609852232SMichael S. Tsirkin * originating from @src_file. 1,2,4 or 8 byte unsigned
5709852232SMichael S. Tsirkin * addition is used depending on @pointer.size.
5809852232SMichael S. Tsirkin */
5909852232SMichael S. Tsirkin struct {
6009852232SMichael S. Tsirkin char dest_file[BIOS_LINKER_LOADER_FILESZ];
6109852232SMichael S. Tsirkin char src_file[BIOS_LINKER_LOADER_FILESZ];
6209852232SMichael S. Tsirkin uint32_t offset;
6309852232SMichael S. Tsirkin uint8_t size;
6409852232SMichael S. Tsirkin } pointer;
6509852232SMichael S. Tsirkin
6609852232SMichael S. Tsirkin /*
6709852232SMichael S. Tsirkin * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by
6809852232SMichael S. Tsirkin * @cksum_start and @cksum_length fields,
6909852232SMichael S. Tsirkin * and then add the value at @cksum.offset.
7009852232SMichael S. Tsirkin * Checksum simply sums -X for each byte X in the range
7109852232SMichael S. Tsirkin * using 8-bit math.
7209852232SMichael S. Tsirkin */
7309852232SMichael S. Tsirkin struct {
7409852232SMichael S. Tsirkin char file[BIOS_LINKER_LOADER_FILESZ];
7509852232SMichael S. Tsirkin uint32_t offset;
7609852232SMichael S. Tsirkin uint32_t start;
7709852232SMichael S. Tsirkin uint32_t length;
7809852232SMichael S. Tsirkin } cksum;
7909852232SMichael S. Tsirkin
80489886d1SBen Warren /*
81489886d1SBen Warren * COMMAND_WRITE_POINTER - write the fw_cfg file (originating from
82489886d1SBen Warren * @dest_file) at @wr_pointer.offset, by adding a pointer to
83489886d1SBen Warren * @src_offset within the table originating from @src_file.
84489886d1SBen Warren * 1,2,4 or 8 byte unsigned addition is used depending on
85489886d1SBen Warren * @wr_pointer.size.
86489886d1SBen Warren */
87489886d1SBen Warren struct {
88489886d1SBen Warren char dest_file[BIOS_LINKER_LOADER_FILESZ];
89489886d1SBen Warren char src_file[BIOS_LINKER_LOADER_FILESZ];
90489886d1SBen Warren uint32_t dst_offset;
91489886d1SBen Warren uint32_t src_offset;
92489886d1SBen Warren uint8_t size;
93489886d1SBen Warren } wr_pointer;
94489886d1SBen Warren
9509852232SMichael S. Tsirkin /* padding */
9609852232SMichael S. Tsirkin char pad[124];
9709852232SMichael S. Tsirkin };
9809852232SMichael S. Tsirkin } QEMU_PACKED;
9909852232SMichael S. Tsirkin typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry;
10009852232SMichael S. Tsirkin
10109852232SMichael S. Tsirkin enum {
10209852232SMichael S. Tsirkin BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1,
10309852232SMichael S. Tsirkin BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2,
10409852232SMichael S. Tsirkin BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3,
105489886d1SBen Warren BIOS_LINKER_LOADER_COMMAND_WRITE_POINTER = 0x4,
10609852232SMichael S. Tsirkin };
10709852232SMichael S. Tsirkin
10809852232SMichael S. Tsirkin enum {
10909852232SMichael S. Tsirkin BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1,
11009852232SMichael S. Tsirkin BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
11109852232SMichael S. Tsirkin };
11209852232SMichael S. Tsirkin
113b54ca0c3SMichael S. Tsirkin /*
114ad9671b8SIgor Mammedov * BiosLinkerFileEntry:
115ad9671b8SIgor Mammedov *
116ad9671b8SIgor Mammedov * An internal type used for book-keeping file entries
117ad9671b8SIgor Mammedov */
118ad9671b8SIgor Mammedov typedef struct BiosLinkerFileEntry {
119ad9671b8SIgor Mammedov char *name; /* file name */
120ad9671b8SIgor Mammedov GArray *blob; /* data accosiated with @name */
121ad9671b8SIgor Mammedov } BiosLinkerFileEntry;
122ad9671b8SIgor Mammedov
123ad9671b8SIgor Mammedov /*
1240e9b9edaSIgor Mammedov * bios_linker_loader_init: allocate a new linker object instance.
125b54ca0c3SMichael S. Tsirkin *
126b54ca0c3SMichael S. Tsirkin * After initialization, linker commands can be added, and will
1270e9b9edaSIgor Mammedov * be stored in the linker.cmd_blob array.
128b54ca0c3SMichael S. Tsirkin */
bios_linker_loader_init(void)1290e9b9edaSIgor Mammedov BIOSLinker *bios_linker_loader_init(void)
13009852232SMichael S. Tsirkin {
1310e9b9edaSIgor Mammedov BIOSLinker *linker = g_new(BIOSLinker, 1);
1320e9b9edaSIgor Mammedov
1330e9b9edaSIgor Mammedov linker->cmd_blob = g_array_new(false, true /* clear */, 1);
134ad9671b8SIgor Mammedov linker->file_list = g_array_new(false, true /* clear */,
135ad9671b8SIgor Mammedov sizeof(BiosLinkerFileEntry));
1360e9b9edaSIgor Mammedov return linker;
13709852232SMichael S. Tsirkin }
13809852232SMichael S. Tsirkin
1398cc87c31SIgor Mammedov /* Free linker wrapper */
bios_linker_loader_cleanup(BIOSLinker * linker)1408cc87c31SIgor Mammedov void bios_linker_loader_cleanup(BIOSLinker *linker)
14109852232SMichael S. Tsirkin {
142ad9671b8SIgor Mammedov int i;
143ad9671b8SIgor Mammedov BiosLinkerFileEntry *entry;
1448cc87c31SIgor Mammedov
1458cc87c31SIgor Mammedov g_array_free(linker->cmd_blob, true);
1460e9b9edaSIgor Mammedov
147ad9671b8SIgor Mammedov for (i = 0; i < linker->file_list->len; i++) {
148ad9671b8SIgor Mammedov entry = &g_array_index(linker->file_list, BiosLinkerFileEntry, i);
149ad9671b8SIgor Mammedov g_free(entry->name);
150ad9671b8SIgor Mammedov }
151ad9671b8SIgor Mammedov g_array_free(linker->file_list, true);
1520e9b9edaSIgor Mammedov g_free(linker);
15309852232SMichael S. Tsirkin }
15409852232SMichael S. Tsirkin
155ad9671b8SIgor Mammedov static const BiosLinkerFileEntry *
bios_linker_find_file(const BIOSLinker * linker,const char * name)156ad9671b8SIgor Mammedov bios_linker_find_file(const BIOSLinker *linker, const char *name)
157ad9671b8SIgor Mammedov {
158ad9671b8SIgor Mammedov int i;
159ad9671b8SIgor Mammedov BiosLinkerFileEntry *entry;
160ad9671b8SIgor Mammedov
161ad9671b8SIgor Mammedov for (i = 0; i < linker->file_list->len; i++) {
162ad9671b8SIgor Mammedov entry = &g_array_index(linker->file_list, BiosLinkerFileEntry, i);
163ad9671b8SIgor Mammedov if (!strcmp(entry->name, name)) {
164ad9671b8SIgor Mammedov return entry;
165ad9671b8SIgor Mammedov }
166ad9671b8SIgor Mammedov }
167ad9671b8SIgor Mammedov return NULL;
168ad9671b8SIgor Mammedov }
169ad9671b8SIgor Mammedov
170b54ca0c3SMichael S. Tsirkin /*
171c8389550SMarc-André Lureau * board code must realize fw_cfg first, as a fixed device, before
172c8389550SMarc-André Lureau * another device realize function call bios_linker_loader_can_write_pointer()
173c8389550SMarc-André Lureau */
bios_linker_loader_can_write_pointer(void)174c8389550SMarc-André Lureau bool bios_linker_loader_can_write_pointer(void)
175c8389550SMarc-André Lureau {
176c8389550SMarc-André Lureau FWCfgState *fw_cfg = fw_cfg_find();
177c8389550SMarc-André Lureau return fw_cfg && fw_cfg_dma_enabled(fw_cfg);
178c8389550SMarc-André Lureau }
179c8389550SMarc-André Lureau
180c8389550SMarc-André Lureau /*
181b54ca0c3SMichael S. Tsirkin * bios_linker_loader_alloc: ask guest to load file into guest memory.
182b54ca0c3SMichael S. Tsirkin *
1830e9b9edaSIgor Mammedov * @linker: linker object instance
184ad9671b8SIgor Mammedov * @file_name: name of the file blob to be loaded
185ad9671b8SIgor Mammedov * @file_blob: pointer to blob corresponding to @file_name
186b54ca0c3SMichael S. Tsirkin * @alloc_align: required minimal alignment in bytes. Must be a power of 2.
187b54ca0c3SMichael S. Tsirkin * @alloc_fseg: request allocation in FSEG zone (useful for the RSDP ACPI table)
188b54ca0c3SMichael S. Tsirkin *
189b54ca0c3SMichael S. Tsirkin * Note: this command must precede any other linker command using this file.
190b54ca0c3SMichael S. Tsirkin */
bios_linker_loader_alloc(BIOSLinker * linker,const char * file_name,GArray * file_blob,uint32_t alloc_align,bool alloc_fseg)1910e9b9edaSIgor Mammedov void bios_linker_loader_alloc(BIOSLinker *linker,
192ad9671b8SIgor Mammedov const char *file_name,
193ad9671b8SIgor Mammedov GArray *file_blob,
19409852232SMichael S. Tsirkin uint32_t alloc_align,
19509852232SMichael S. Tsirkin bool alloc_fseg)
19609852232SMichael S. Tsirkin {
19709852232SMichael S. Tsirkin BiosLinkerLoaderEntry entry;
198ad9671b8SIgor Mammedov BiosLinkerFileEntry file = { g_strdup(file_name), file_blob};
19909852232SMichael S. Tsirkin
200b54ca0c3SMichael S. Tsirkin assert(!(alloc_align & (alloc_align - 1)));
201b54ca0c3SMichael S. Tsirkin
202ad9671b8SIgor Mammedov assert(!bios_linker_find_file(linker, file_name));
203ad9671b8SIgor Mammedov g_array_append_val(linker->file_list, file);
204ad9671b8SIgor Mammedov
20509852232SMichael S. Tsirkin memset(&entry, 0, sizeof entry);
206ad9671b8SIgor Mammedov strncpy(entry.alloc.file, file_name, sizeof entry.alloc.file - 1);
20709852232SMichael S. Tsirkin entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE);
20809852232SMichael S. Tsirkin entry.alloc.align = cpu_to_le32(alloc_align);
2091dbfd789SIgor Mammedov entry.alloc.zone = alloc_fseg ? BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG :
2101dbfd789SIgor Mammedov BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH;
21109852232SMichael S. Tsirkin
21209852232SMichael S. Tsirkin /* Alloc entries must come first, so prepend them */
2130e9b9edaSIgor Mammedov g_array_prepend_vals(linker->cmd_blob, &entry, sizeof entry);
21409852232SMichael S. Tsirkin }
21509852232SMichael S. Tsirkin
216b54ca0c3SMichael S. Tsirkin /*
21728213cb6SIgor Mammedov * bios_linker_loader_add_checksum: ask guest to add checksum of ACPI
21828213cb6SIgor Mammedov * table in the specified file at the specified offset.
219b54ca0c3SMichael S. Tsirkin *
220b54ca0c3SMichael S. Tsirkin * Checksum calculation simply sums -X for each byte X in the range
221b54ca0c3SMichael S. Tsirkin * using 8-bit math (i.e. ACPI checksum).
222b54ca0c3SMichael S. Tsirkin *
2230e9b9edaSIgor Mammedov * @linker: linker object instance
224b54ca0c3SMichael S. Tsirkin * @file: file that includes the checksum to be calculated
225b54ca0c3SMichael S. Tsirkin * and the data to be checksummed
22628213cb6SIgor Mammedov * @start_offset, @size: range of data in the file to checksum,
22728213cb6SIgor Mammedov * relative to the start of file blob
22828213cb6SIgor Mammedov * @checksum_offset: location of the checksum to be patched within file blob,
22928213cb6SIgor Mammedov * relative to the start of file blob
230b54ca0c3SMichael S. Tsirkin */
bios_linker_loader_add_checksum(BIOSLinker * linker,const char * file_name,unsigned start_offset,unsigned size,unsigned checksum_offset)231ad9671b8SIgor Mammedov void bios_linker_loader_add_checksum(BIOSLinker *linker, const char *file_name,
23228213cb6SIgor Mammedov unsigned start_offset, unsigned size,
23328213cb6SIgor Mammedov unsigned checksum_offset)
23409852232SMichael S. Tsirkin {
23509852232SMichael S. Tsirkin BiosLinkerLoaderEntry entry;
236ad9671b8SIgor Mammedov const BiosLinkerFileEntry *file = bios_linker_find_file(linker, file_name);
237b54ca0c3SMichael S. Tsirkin
23828213cb6SIgor Mammedov assert(file);
23928213cb6SIgor Mammedov assert(start_offset < file->blob->len);
240ad9671b8SIgor Mammedov assert(start_offset + size <= file->blob->len);
24128213cb6SIgor Mammedov assert(checksum_offset >= start_offset);
24228213cb6SIgor Mammedov assert(checksum_offset + 1 <= start_offset + size);
24309852232SMichael S. Tsirkin
24428213cb6SIgor Mammedov *(file->blob->data + checksum_offset) = 0;
24509852232SMichael S. Tsirkin memset(&entry, 0, sizeof entry);
246ad9671b8SIgor Mammedov strncpy(entry.cksum.file, file_name, sizeof entry.cksum.file - 1);
24709852232SMichael S. Tsirkin entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM);
248b54ca0c3SMichael S. Tsirkin entry.cksum.offset = cpu_to_le32(checksum_offset);
249b54ca0c3SMichael S. Tsirkin entry.cksum.start = cpu_to_le32(start_offset);
25009852232SMichael S. Tsirkin entry.cksum.length = cpu_to_le32(size);
25109852232SMichael S. Tsirkin
2520e9b9edaSIgor Mammedov g_array_append_vals(linker->cmd_blob, &entry, sizeof entry);
25309852232SMichael S. Tsirkin }
25409852232SMichael S. Tsirkin
255b54ca0c3SMichael S. Tsirkin /*
2564678124bSIgor Mammedov * bios_linker_loader_add_pointer: ask guest to patch address in
2574678124bSIgor Mammedov * destination file with a pointer to source file
258b54ca0c3SMichael S. Tsirkin *
2590e9b9edaSIgor Mammedov * @linker: linker object instance
260b54ca0c3SMichael S. Tsirkin * @dest_file: destination file that must be changed
2614678124bSIgor Mammedov * @dst_patched_offset: location within destination file blob to be patched
2624678124bSIgor Mammedov * with the pointer to @src_file+@src_offset (i.e. source
2634678124bSIgor Mammedov * blob allocated in guest memory + @src_offset), in bytes
2644678124bSIgor Mammedov * @dst_patched_offset_size: size of the pointer to be patched
2654678124bSIgor Mammedov * at @dst_patched_offset in @dest_file blob, in bytes
266b54ca0c3SMichael S. Tsirkin * @src_file: source file who's address must be taken
2674678124bSIgor Mammedov * @src_offset: location within source file blob to which
2684678124bSIgor Mammedov * @dest_file+@dst_patched_offset will point to after
2694678124bSIgor Mammedov * firmware's executed ADD_POINTER command
270b54ca0c3SMichael S. Tsirkin */
bios_linker_loader_add_pointer(BIOSLinker * linker,const char * dest_file,uint32_t dst_patched_offset,uint8_t dst_patched_size,const char * src_file,uint32_t src_offset)2710e9b9edaSIgor Mammedov void bios_linker_loader_add_pointer(BIOSLinker *linker,
27209852232SMichael S. Tsirkin const char *dest_file,
2734678124bSIgor Mammedov uint32_t dst_patched_offset,
2744678124bSIgor Mammedov uint8_t dst_patched_size,
27509852232SMichael S. Tsirkin const char *src_file,
2764678124bSIgor Mammedov uint32_t src_offset)
27709852232SMichael S. Tsirkin {
2784678124bSIgor Mammedov uint64_t le_src_offset;
27909852232SMichael S. Tsirkin BiosLinkerLoaderEntry entry;
2804678124bSIgor Mammedov const BiosLinkerFileEntry *dst_file =
2814678124bSIgor Mammedov bios_linker_find_file(linker, dest_file);
2824678124bSIgor Mammedov const BiosLinkerFileEntry *source_file =
2834678124bSIgor Mammedov bios_linker_find_file(linker, src_file);
284b54ca0c3SMichael S. Tsirkin
285*22132828SLiam Merwick assert(dst_file);
286*22132828SLiam Merwick assert(source_file);
2874678124bSIgor Mammedov assert(dst_patched_offset < dst_file->blob->len);
2884678124bSIgor Mammedov assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len);
2894678124bSIgor Mammedov assert(src_offset < source_file->blob->len);
29009852232SMichael S. Tsirkin
29109852232SMichael S. Tsirkin memset(&entry, 0, sizeof entry);
29209852232SMichael S. Tsirkin strncpy(entry.pointer.dest_file, dest_file,
29309852232SMichael S. Tsirkin sizeof entry.pointer.dest_file - 1);
29409852232SMichael S. Tsirkin strncpy(entry.pointer.src_file, src_file,
29509852232SMichael S. Tsirkin sizeof entry.pointer.src_file - 1);
29609852232SMichael S. Tsirkin entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER);
2974678124bSIgor Mammedov entry.pointer.offset = cpu_to_le32(dst_patched_offset);
2984678124bSIgor Mammedov entry.pointer.size = dst_patched_size;
2994678124bSIgor Mammedov assert(dst_patched_size == 1 || dst_patched_size == 2 ||
3004678124bSIgor Mammedov dst_patched_size == 4 || dst_patched_size == 8);
3014678124bSIgor Mammedov
3024678124bSIgor Mammedov le_src_offset = cpu_to_le64(src_offset);
3034678124bSIgor Mammedov memcpy(dst_file->blob->data + dst_patched_offset,
3044678124bSIgor Mammedov &le_src_offset, dst_patched_size);
30509852232SMichael S. Tsirkin
3060e9b9edaSIgor Mammedov g_array_append_vals(linker->cmd_blob, &entry, sizeof entry);
30709852232SMichael S. Tsirkin }
308489886d1SBen Warren
309489886d1SBen Warren /*
310489886d1SBen Warren * bios_linker_loader_write_pointer: ask guest to write a pointer to the
311489886d1SBen Warren * source file into the destination file, and write it back to QEMU via
312489886d1SBen Warren * fw_cfg DMA.
313489886d1SBen Warren *
314489886d1SBen Warren * @linker: linker object instance
315489886d1SBen Warren * @dest_file: destination file that must be written
316489886d1SBen Warren * @dst_patched_offset: location within destination file blob to be patched
317489886d1SBen Warren * with the pointer to @src_file, in bytes
318489886d1SBen Warren * @dst_patched_offset_size: size of the pointer to be patched
319489886d1SBen Warren * at @dst_patched_offset in @dest_file blob, in bytes
320489886d1SBen Warren * @src_file: source file who's address must be taken
321489886d1SBen Warren * @src_offset: location within source file blob to which
322489886d1SBen Warren * @dest_file+@dst_patched_offset will point to after
323489886d1SBen Warren * firmware's executed WRITE_POINTER command
324489886d1SBen Warren */
bios_linker_loader_write_pointer(BIOSLinker * linker,const char * dest_file,uint32_t dst_patched_offset,uint8_t dst_patched_size,const char * src_file,uint32_t src_offset)325489886d1SBen Warren void bios_linker_loader_write_pointer(BIOSLinker *linker,
326489886d1SBen Warren const char *dest_file,
327489886d1SBen Warren uint32_t dst_patched_offset,
328489886d1SBen Warren uint8_t dst_patched_size,
329489886d1SBen Warren const char *src_file,
330489886d1SBen Warren uint32_t src_offset)
331489886d1SBen Warren {
332489886d1SBen Warren BiosLinkerLoaderEntry entry;
333489886d1SBen Warren const BiosLinkerFileEntry *source_file =
334489886d1SBen Warren bios_linker_find_file(linker, src_file);
335489886d1SBen Warren
336489886d1SBen Warren assert(source_file);
337489886d1SBen Warren assert(src_offset < source_file->blob->len);
338489886d1SBen Warren memset(&entry, 0, sizeof entry);
339489886d1SBen Warren strncpy(entry.wr_pointer.dest_file, dest_file,
340489886d1SBen Warren sizeof entry.wr_pointer.dest_file - 1);
341489886d1SBen Warren strncpy(entry.wr_pointer.src_file, src_file,
342489886d1SBen Warren sizeof entry.wr_pointer.src_file - 1);
343489886d1SBen Warren entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_WRITE_POINTER);
344489886d1SBen Warren entry.wr_pointer.dst_offset = cpu_to_le32(dst_patched_offset);
345489886d1SBen Warren entry.wr_pointer.src_offset = cpu_to_le32(src_offset);
346489886d1SBen Warren entry.wr_pointer.size = dst_patched_size;
347489886d1SBen Warren assert(dst_patched_size == 1 || dst_patched_size == 2 ||
348489886d1SBen Warren dst_patched_size == 4 || dst_patched_size == 8);
349489886d1SBen Warren
350489886d1SBen Warren g_array_append_vals(linker->cmd_blob, &entry, sizeof entry);
351489886d1SBen Warren }
352