Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / seabios / src / fw / romfile_loader.c
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 "output.h" // warn_*
8
9 struct romfile_loader_file {
10     struct romfile_s *file;
11     void *data;
12 };
13 struct romfile_loader_files {
14     int nfiles;
15     struct romfile_loader_file files[];
16 };
17
18 static struct romfile_loader_file *
19 romfile_loader_find(const char *name,
20                     struct romfile_loader_files *files)
21 {
22     int i;
23     if (name[ROMFILE_LOADER_FILESZ - 1])
24         return NULL;
25     for (i = 0; i < files->nfiles; ++i)
26         if (!strcmp(files->files[i].file->name, name))
27             return &files->files[i];
28     return NULL;
29 }
30
31 static void romfile_loader_allocate(struct romfile_loader_entry_s *entry,
32                                     struct romfile_loader_files *files)
33 {
34     struct zone_s *zone;
35     struct romfile_loader_file *file = &files->files[files->nfiles];
36     void *data;
37     int ret;
38     unsigned alloc_align = le32_to_cpu(entry->alloc_align);
39
40     if (alloc_align & (alloc_align - 1))
41         goto err;
42
43     switch (entry->alloc_zone) {
44         case ROMFILE_LOADER_ALLOC_ZONE_HIGH:
45             zone = &ZoneHigh;
46             break;
47         case ROMFILE_LOADER_ALLOC_ZONE_FSEG:
48             zone = &ZoneFSeg;
49             break;
50         default:
51             goto err;
52     }
53     if (alloc_align < MALLOC_MIN_ALIGN)
54         alloc_align = MALLOC_MIN_ALIGN;
55     if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1])
56         goto err;
57     file->file = romfile_find(entry->alloc_file);
58     if (!file->file || !file->file->size)
59         return;
60     data = _malloc(zone, file->file->size, alloc_align);
61     if (!data) {
62         warn_noalloc();
63         return;
64     }
65     ret = file->file->copy(file->file, data, file->file->size);
66     if (ret != file->file->size)
67         goto file_err;
68     file->data = data;
69     files->nfiles++;
70     return;
71
72 file_err:
73     free(data);
74 err:
75     warn_internalerror();
76 }
77
78 static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry,
79                                        struct romfile_loader_files *files)
80 {
81     struct romfile_loader_file *dest_file;
82     struct romfile_loader_file *src_file;
83     unsigned offset = le32_to_cpu(entry->pointer_offset);
84     u64 pointer = 0;
85
86     dest_file = romfile_loader_find(entry->pointer_dest_file, files);
87     src_file = romfile_loader_find(entry->pointer_src_file, files);
88
89     if (!dest_file || !src_file || !dest_file->data || !src_file->data ||
90         offset + entry->pointer_size < offset ||
91         offset + entry->pointer_size > dest_file->file->size ||
92         entry->pointer_size < 1 || entry->pointer_size > 8 ||
93         entry->pointer_size & (entry->pointer_size - 1))
94         goto err;
95
96     memcpy(&pointer, dest_file->data + offset, entry->pointer_size);
97     pointer = le64_to_cpu(pointer);
98     pointer += (unsigned long)src_file->data;
99     pointer = cpu_to_le64(pointer);
100     memcpy(dest_file->data + offset, &pointer, entry->pointer_size);
101
102     return;
103 err:
104     warn_internalerror();
105 }
106
107 static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry,
108                                         struct romfile_loader_files *files)
109 {
110     struct romfile_loader_file *file;
111     unsigned offset = le32_to_cpu(entry->cksum_offset);
112     unsigned start = le32_to_cpu(entry->cksum_start);
113     unsigned len = le32_to_cpu(entry->cksum_length);
114     u8 *data;
115
116     file = romfile_loader_find(entry->cksum_file, files);
117
118     if (!file || !file->data || offset >= file->file->size ||
119         start + len < start || start + len > file->file->size)
120         goto err;
121
122     data = file->data + offset;
123     *data -= checksum(file->data + start, len);
124
125     return;
126 err:
127     warn_internalerror();
128 }
129
130 int romfile_loader_execute(const char *name)
131 {
132     struct romfile_loader_entry_s *entry;
133     int size, offset = 0, nfiles;
134     struct romfile_loader_files *files;
135     void *data = romfile_loadfile(name, &size);
136     if (!data)
137         return -1;
138
139     if (size % sizeof(*entry)) {
140         warn_internalerror();
141         goto err;
142     }
143
144     /* (over)estimate the number of files to load. */
145     nfiles = size / sizeof(*entry);
146     files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0]));
147     if (!files) {
148         warn_noalloc();
149         goto err;
150     }
151     files->nfiles = 0;
152
153     for (offset = 0; offset < size; offset += sizeof(*entry)) {
154         entry = data + offset;
155         switch (le32_to_cpu(entry->command)) {
156                 case ROMFILE_LOADER_COMMAND_ALLOCATE:
157                         romfile_loader_allocate(entry, files);
158                         break;
159                 case ROMFILE_LOADER_COMMAND_ADD_POINTER:
160                         romfile_loader_add_pointer(entry, files);
161                         break;
162                 case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM:
163                         romfile_loader_add_checksum(entry, files);
164                 default:
165                         /* Skip commands that we don't recognize. */
166                         break;
167         }
168     }
169
170     free(files);
171     free(data);
172     return 0;
173
174 err:
175     free(data);
176     return -1;
177 }