Improved initrd support for mips.
[qemu] / loader.c
1 /*
2  * QEMU Executable loader
3  * 
4  * Copyright (c) 2006 Fabrice Bellard
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "vl.h"
25 #include "disas.h"
26 #include "uboot_image.h"
27
28 /* return the size or -1 if error */
29 int get_image_size(const char *filename)
30 {
31     int fd, size;
32     fd = open(filename, O_RDONLY | O_BINARY);
33     if (fd < 0)
34         return -1;
35     size = lseek(fd, 0, SEEK_END);
36     close(fd);
37     return size;
38 }
39
40 /* return the size or -1 if error */
41 int load_image(const char *filename, uint8_t *addr)
42 {
43     int fd, size;
44     fd = open(filename, O_RDONLY | O_BINARY);
45     if (fd < 0)
46         return -1;
47     size = lseek(fd, 0, SEEK_END);
48     lseek(fd, 0, SEEK_SET);
49     if (read(fd, addr, size) != size) {
50         close(fd);
51         return -1;
52     }
53     close(fd);
54     return size;
55 }
56
57 /* A.OUT loader */
58
59 struct exec
60 {
61   uint32_t a_info;   /* Use macros N_MAGIC, etc for access */
62   uint32_t a_text;   /* length of text, in bytes */
63   uint32_t a_data;   /* length of data, in bytes */
64   uint32_t a_bss;    /* length of uninitialized data area, in bytes */
65   uint32_t a_syms;   /* length of symbol table data in file, in bytes */
66   uint32_t a_entry;  /* start address */
67   uint32_t a_trsize; /* length of relocation info for text, in bytes */
68   uint32_t a_drsize; /* length of relocation info for data, in bytes */
69 };
70
71 #ifdef BSWAP_NEEDED
72 static void bswap_ahdr(struct exec *e)
73 {
74     bswap32s(&e->a_info);
75     bswap32s(&e->a_text);
76     bswap32s(&e->a_data);
77     bswap32s(&e->a_bss);
78     bswap32s(&e->a_syms);
79     bswap32s(&e->a_entry);
80     bswap32s(&e->a_trsize);
81     bswap32s(&e->a_drsize);
82 }
83 #else
84 #define bswap_ahdr(x) do { } while (0)
85 #endif
86
87 #define N_MAGIC(exec) ((exec).a_info & 0xffff)
88 #define OMAGIC 0407
89 #define NMAGIC 0410
90 #define ZMAGIC 0413
91 #define QMAGIC 0314
92 #define _N_HDROFF(x) (1024 - sizeof (struct exec))
93 #define N_TXTOFF(x)                                                     \
94     (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) :     \
95      (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
96 #define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0)
97 #define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
98 #define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1))
99
100 #define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
101
102 #define N_DATADDR(x) \
103     (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
104      : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
105
106
107 int load_aout(const char *filename, uint8_t *addr)
108 {
109     int fd, size, ret;
110     struct exec e;
111     uint32_t magic;
112
113     fd = open(filename, O_RDONLY | O_BINARY);
114     if (fd < 0)
115         return -1;
116
117     size = read(fd, &e, sizeof(e));
118     if (size < 0)
119         goto fail;
120
121     bswap_ahdr(&e);
122
123     magic = N_MAGIC(e);
124     switch (magic) {
125     case ZMAGIC:
126     case QMAGIC:
127     case OMAGIC:
128         lseek(fd, N_TXTOFF(e), SEEK_SET);
129         size = read(fd, addr, e.a_text + e.a_data);
130         if (size < 0)
131             goto fail;
132         break;
133     case NMAGIC:
134         lseek(fd, N_TXTOFF(e), SEEK_SET);
135         size = read(fd, addr, e.a_text);
136         if (size < 0)
137             goto fail;
138         ret = read(fd, addr + N_DATADDR(e), e.a_data);
139         if (ret < 0)
140             goto fail;
141         size += ret;
142         break;
143     default:
144         goto fail;
145     }
146     close(fd);
147     return size;
148  fail:
149     close(fd);
150     return -1;
151 }
152
153 /* ELF loader */
154
155 static void *load_at(int fd, int offset, int size)
156 {
157     void *ptr;
158     if (lseek(fd, offset, SEEK_SET) < 0)
159         return NULL;
160     ptr = qemu_malloc(size);
161     if (!ptr)
162         return NULL;
163     if (read(fd, ptr, size) != size) {
164         qemu_free(ptr);
165         return NULL;
166     }
167     return ptr;
168 }
169
170
171 #define ELF_CLASS   ELFCLASS32
172 #include "elf.h"
173
174 #define SZ              32
175 #define elf_word        uint32_t
176 #define bswapSZs        bswap32s
177 #include "elf_ops.h"
178
179 #undef elfhdr
180 #undef elf_phdr
181 #undef elf_shdr
182 #undef elf_sym
183 #undef elf_note
184 #undef elf_word
185 #undef bswapSZs
186 #undef SZ
187 #define elfhdr          elf64_hdr
188 #define elf_phdr        elf64_phdr
189 #define elf_note        elf64_note
190 #define elf_shdr        elf64_shdr
191 #define elf_sym         elf64_sym
192 #define elf_word        uint64_t
193 #define bswapSZs        bswap64s
194 #define SZ              64
195 #include "elf_ops.h"
196
197 /* return < 0 if error, otherwise the number of bytes loaded in memory */
198 int load_elf(const char *filename, int64_t virt_to_phys_addend,
199              uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
200 {
201     int fd, data_order, host_data_order, must_swab, ret;
202     uint8_t e_ident[EI_NIDENT];
203
204     fd = open(filename, O_RDONLY | O_BINARY);
205     if (fd < 0) {
206         perror(filename);
207         return -1;
208     }
209     if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
210         goto fail;
211     if (e_ident[0] != ELFMAG0 ||
212         e_ident[1] != ELFMAG1 ||
213         e_ident[2] != ELFMAG2 ||
214         e_ident[3] != ELFMAG3)
215         goto fail;
216 #ifdef WORDS_BIGENDIAN
217     data_order = ELFDATA2MSB;
218 #else
219     data_order = ELFDATA2LSB;
220 #endif
221     must_swab = data_order != e_ident[EI_DATA];
222
223 #ifdef TARGET_WORDS_BIGENDIAN
224     host_data_order = ELFDATA2MSB;
225 #else
226     host_data_order = ELFDATA2LSB;
227 #endif
228     if (host_data_order != e_ident[EI_DATA])
229         return -1;
230
231     lseek(fd, 0, SEEK_SET);
232     if (e_ident[EI_CLASS] == ELFCLASS64) {
233         ret = load_elf64(fd, virt_to_phys_addend, must_swab, pentry,
234                          lowaddr, highaddr);
235     } else {
236         ret = load_elf32(fd, virt_to_phys_addend, must_swab, pentry,
237                          lowaddr, highaddr);
238     }
239
240     close(fd);
241     return ret;
242
243  fail:
244     close(fd);
245     return -1;
246 }
247
248 static void bswap_uboot_header(uboot_image_header_t *hdr)
249 {
250 #ifndef WORDS_BIGENDIAN
251     bswap32s(&hdr->ih_magic);
252     bswap32s(&hdr->ih_hcrc);
253     bswap32s(&hdr->ih_time);
254     bswap32s(&hdr->ih_size);
255     bswap32s(&hdr->ih_load);
256     bswap32s(&hdr->ih_ep);
257     bswap32s(&hdr->ih_dcrc);
258 #endif
259 }
260
261 /* Load a U-Boot image.  */
262 int load_uboot(const char *filename, target_ulong *ep, int *is_linux)
263 {
264     
265     int fd;
266     int size;
267     uboot_image_header_t h;
268     uboot_image_header_t *hdr = &h;
269     uint8_t *data = NULL;
270
271     fd = open(filename, O_RDONLY | O_BINARY);
272     if (fd < 0)
273         return -1;
274
275     size = read(fd, hdr, sizeof(uboot_image_header_t));
276     if (size < 0)
277         goto fail;
278
279     bswap_uboot_header(hdr);
280
281     if (hdr->ih_magic != IH_MAGIC)
282         goto fail;
283
284     /* TODO: Implement Multi-File images.  */
285     if (hdr->ih_type == IH_TYPE_MULTI) {
286         fprintf(stderr, "Unable to load multi-file u-boot images\n");
287         goto fail;
288     }
289
290     /* TODO: Implement compressed images.  */
291     if (hdr->ih_comp != IH_COMP_NONE) {
292         fprintf(stderr, "Unable to load compressed u-boot images\n");
293         goto fail;
294     }
295
296     /* TODO: Check CPU type.  */
297     if (is_linux) {
298         if (hdr->ih_type == IH_TYPE_KERNEL && hdr->ih_os == IH_OS_LINUX)
299             *is_linux = 1;
300         else
301             *is_linux = 0;
302     }
303
304     *ep = hdr->ih_ep;
305     data = qemu_malloc(hdr->ih_size);
306     if (!data)
307         goto fail;
308
309     if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
310         fprintf(stderr, "Error reading file\n");
311         goto fail;
312     }
313
314     cpu_physical_memory_write_rom(hdr->ih_load, data, hdr->ih_size);
315
316     return hdr->ih_size;
317
318 fail:
319     if (data)
320         qemu_free(data);
321     close(fd);
322     return -1;
323 }