~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/seabios/src/fw/romfile_loader.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "romfile_loader.h"
 
2
#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX
 
3
#include "util.h" // checksum
 
4
#include "string.h" // strcmp
 
5
#include "romfile.h" // struct romfile_s
 
6
#include "malloc.h" // Zone*, _malloc
 
7
#include "list.h" // struct hlist_node
 
8
#include "output.h" // warn_*
 
9
#include "paravirt.h" // qemu_cfg_write_file
 
10
 
 
11
struct romfile_loader_file {
 
12
    struct romfile_s *file;
 
13
    void *data;
 
14
};
 
15
struct romfile_loader_files {
 
16
    int nfiles;
 
17
    struct romfile_loader_file files[];
 
18
};
 
19
 
 
20
// Data structures for storing "write pointer" entries for possible replay
 
21
struct romfile_wr_pointer_entry {
 
22
    u64 pointer;
 
23
    u32 offset;
 
24
    u16 key;
 
25
    u8 ptr_size;
 
26
    struct hlist_node node;
 
27
};
 
28
static struct hlist_head romfile_pointer_list;
 
29
 
 
30
static struct romfile_loader_file *
 
31
romfile_loader_find(const char *name,
 
32
                    struct romfile_loader_files *files)
 
33
{
 
34
    int i;
 
35
    if (name[ROMFILE_LOADER_FILESZ - 1])
 
36
        return NULL;
 
37
    for (i = 0; i < files->nfiles; ++i)
 
38
        if (!strcmp(files->files[i].file->name, name))
 
39
            return &files->files[i];
 
40
    return NULL;
 
41
}
 
42
 
 
43
// Replay "write pointer" entries back to QEMU
 
44
void romfile_fw_cfg_resume(void)
 
45
{
 
46
    if (!CONFIG_QEMU)
 
47
        return;
 
48
 
 
49
    struct romfile_wr_pointer_entry *entry;
 
50
    hlist_for_each_entry(entry, &romfile_pointer_list, node) {
 
51
        qemu_cfg_write_file_simple(&entry->pointer, entry->key,
 
52
                                   entry->offset, entry->ptr_size);
 
53
    }
 
54
}
 
55
 
 
56
static void romfile_loader_allocate(struct romfile_loader_entry_s *entry,
 
57
                                    struct romfile_loader_files *files)
 
58
{
 
59
    struct zone_s *zone;
 
60
    struct romfile_loader_file *file = &files->files[files->nfiles];
 
61
    void *data;
 
62
    int ret;
 
63
    unsigned alloc_align = le32_to_cpu(entry->alloc.align);
 
64
 
 
65
    if (alloc_align & (alloc_align - 1))
 
66
        goto err;
 
67
 
 
68
    switch (entry->alloc.zone) {
 
69
        case ROMFILE_LOADER_ALLOC_ZONE_HIGH:
 
70
            zone = &ZoneHigh;
 
71
            break;
 
72
        case ROMFILE_LOADER_ALLOC_ZONE_FSEG:
 
73
            zone = &ZoneFSeg;
 
74
            break;
 
75
        default:
 
76
            goto err;
 
77
    }
 
78
    if (alloc_align < MALLOC_MIN_ALIGN)
 
79
        alloc_align = MALLOC_MIN_ALIGN;
 
80
    if (entry->alloc.file[ROMFILE_LOADER_FILESZ - 1])
 
81
        goto err;
 
82
    file->file = romfile_find(entry->alloc.file);
 
83
    if (!file->file || !file->file->size)
 
84
        return;
 
85
    data = _malloc(zone, file->file->size, alloc_align);
 
86
    if (!data) {
 
87
        warn_noalloc();
 
88
        return;
 
89
    }
 
90
    ret = file->file->copy(file->file, data, file->file->size);
 
91
    if (ret != file->file->size)
 
92
        goto file_err;
 
93
    file->data = data;
 
94
    files->nfiles++;
 
95
    return;
 
96
 
 
97
file_err:
 
98
    free(data);
 
99
err:
 
100
    warn_internalerror();
 
101
}
 
102
 
 
103
static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry,
 
104
                                       struct romfile_loader_files *files)
 
105
{
 
106
    struct romfile_loader_file *dest_file;
 
107
    struct romfile_loader_file *src_file;
 
108
    unsigned offset = le32_to_cpu(entry->pointer.offset);
 
109
    u64 pointer = 0;
 
110
 
 
111
    dest_file = romfile_loader_find(entry->pointer.dest_file, files);
 
112
    src_file = romfile_loader_find(entry->pointer.src_file, files);
 
113
 
 
114
    if (!dest_file || !src_file || !dest_file->data || !src_file->data ||
 
115
        offset + entry->pointer.size < offset ||
 
116
        offset + entry->pointer.size > dest_file->file->size ||
 
117
        entry->pointer.size < 1 || entry->pointer.size > 8 ||
 
118
        entry->pointer.size & (entry->pointer.size - 1))
 
119
        goto err;
 
120
 
 
121
    memcpy(&pointer, dest_file->data + offset, entry->pointer.size);
 
122
    pointer = le64_to_cpu(pointer);
 
123
    pointer += (unsigned long)src_file->data;
 
124
    pointer = cpu_to_le64(pointer);
 
125
    memcpy(dest_file->data + offset, &pointer, entry->pointer.size);
 
126
 
 
127
    return;
 
128
err:
 
129
    warn_internalerror();
 
130
}
 
131
 
 
132
static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry,
 
133
                                        struct romfile_loader_files *files)
 
134
{
 
135
    struct romfile_loader_file *file;
 
136
    unsigned offset = le32_to_cpu(entry->cksum.offset);
 
137
    unsigned start = le32_to_cpu(entry->cksum.start);
 
138
    unsigned len = le32_to_cpu(entry->cksum.length);
 
139
    u8 *data;
 
140
 
 
141
    file = romfile_loader_find(entry->cksum.file, files);
 
142
 
 
143
    if (!file || !file->data || offset >= file->file->size ||
 
144
        start + len < start || start + len > file->file->size)
 
145
        goto err;
 
146
 
 
147
    data = file->data + offset;
 
148
    *data -= checksum(file->data + start, len);
 
149
 
 
150
    return;
 
151
err:
 
152
    warn_internalerror();
 
153
}
 
154
 
 
155
static void romfile_loader_write_pointer(struct romfile_loader_entry_s *entry,
 
156
                                         struct romfile_loader_files *files)
 
157
{
 
158
    struct romfile_s *dest_file;
 
159
    struct romfile_loader_file *src_file;
 
160
    unsigned dst_offset = le32_to_cpu(entry->wr_pointer.dst_offset);
 
161
    unsigned src_offset = le32_to_cpu(entry->wr_pointer.src_offset);
 
162
    u64 pointer = 0;
 
163
 
 
164
    /* Writing back to a file that may not be loaded in RAM */
 
165
    dest_file = romfile_find(entry->wr_pointer.dest_file);
 
166
    src_file = romfile_loader_find(entry->wr_pointer.src_file, files);
 
167
 
 
168
    if (!dest_file || !src_file || !src_file->data ||
 
169
        dst_offset + entry->wr_pointer.size < dst_offset ||
 
170
        dst_offset + entry->wr_pointer.size > dest_file->size ||
 
171
        src_offset >= src_file->file->size ||
 
172
        entry->wr_pointer.size < 1 || entry->wr_pointer.size > 8 ||
 
173
        entry->wr_pointer.size & (entry->wr_pointer.size - 1)) {
 
174
        goto err;
 
175
    }
 
176
 
 
177
    pointer = (unsigned long)src_file->data + src_offset;
 
178
    /* Make sure the pointer fits within wr_pointer.size */
 
179
    if ((entry->wr_pointer.size != sizeof(u64)) &&
 
180
        ((pointer >> (entry->wr_pointer.size * 8)) > 0)) {
 
181
        goto err;
 
182
    }
 
183
    pointer = cpu_to_le64(pointer);
 
184
 
 
185
    /* Only supported on QEMU */
 
186
    if (qemu_cfg_write_file(&pointer, dest_file, dst_offset,
 
187
                            entry->wr_pointer.size) != entry->wr_pointer.size) {
 
188
        goto err;
 
189
    }
 
190
 
 
191
    /* Store the info so it can replayed later if necessary */
 
192
    struct romfile_wr_pointer_entry *store = malloc_high(sizeof(*store));
 
193
    if (!store) {
 
194
        warn_noalloc();
 
195
        return;
 
196
    }
 
197
    store->pointer = pointer;
 
198
    store->key = qemu_get_romfile_key(dest_file);
 
199
    store->offset = dst_offset;
 
200
    store->ptr_size = entry->wr_pointer.size;
 
201
    hlist_add_head(&store->node, &romfile_pointer_list);
 
202
 
 
203
    return;
 
204
 err:
 
205
    warn_internalerror();
 
206
}
 
207
 
 
208
int romfile_loader_execute(const char *name)
 
209
{
 
210
    struct romfile_loader_entry_s *entry;
 
211
    int size, offset = 0, nfiles;
 
212
    struct romfile_loader_files *files;
 
213
    void *data = romfile_loadfile(name, &size);
 
214
    if (!data)
 
215
        return -1;
 
216
 
 
217
    if (size % sizeof(*entry)) {
 
218
        warn_internalerror();
 
219
        goto err;
 
220
    }
 
221
 
 
222
    /* (over)estimate the number of files to load. */
 
223
    nfiles = size / sizeof(*entry);
 
224
    files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0]));
 
225
    if (!files) {
 
226
        warn_noalloc();
 
227
        goto err;
 
228
    }
 
229
    files->nfiles = 0;
 
230
 
 
231
    for (offset = 0; offset < size; offset += sizeof(*entry)) {
 
232
        entry = data + offset;
 
233
        switch (le32_to_cpu(entry->command)) {
 
234
                case ROMFILE_LOADER_COMMAND_ALLOCATE:
 
235
                        romfile_loader_allocate(entry, files);
 
236
                        break;
 
237
                case ROMFILE_LOADER_COMMAND_ADD_POINTER:
 
238
                        romfile_loader_add_pointer(entry, files);
 
239
                        break;
 
240
                case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM:
 
241
                        romfile_loader_add_checksum(entry, files);
 
242
                        break;
 
243
                case ROMFILE_LOADER_COMMAND_WRITE_POINTER:
 
244
                        romfile_loader_write_pointer(entry, files);
 
245
                        break;
 
246
                default:
 
247
                        /* Skip commands that we don't recognize. */
 
248
                        break;
 
249
        }
 
250
    }
 
251
 
 
252
    free(files);
 
253
    free(data);
 
254
    return 0;
 
255
 
 
256
err:
 
257
    free(data);
 
258
    return -1;
 
259
}