1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <unistd.h>
11#include <fcntl.h>
12
13#include <elf/elf.h>
14#include <vka/capops.h>
15#include <cpio/cpio.h>
16#include <sel4utils/sel4_zf_logif.h>
17
18#include <sel4vm/guest_vm.h>
19#include <sel4vm/guest_memory.h>
20#include <sel4vm/guest_ram.h>
21
22#include <sel4vmmplatsupport/guest_image.h>
23
24#define UIMAGE_MAGIC 0x56190527
25#define ZIMAGE_MAGIC 0x016F2818
26#define DTB_MAGIC    0xedfe0dd0
27#define INITRD_GZ_MAGIC 0x8b1f
28
29struct dtb_hdr {
30    uint32_t magic;
31#if 0
32    uint32_t size;
33    uint32_t off_dt_struct;
34    uint32_t off_dt_strings;
35    uint32_t off_mem_rsvmap;
36    uint32_t version;
37    uint32_t comp_version;
38    uint32_t boot_cpuid;
39    uint32_t size_dt_strings;
40    uint32_t size_dt_struct;
41#endif
42};
43
44struct initrd_gz_hdr {
45    uint16_t magic;
46    uint8_t compression;
47    uint8_t flags;
48};
49
50struct zimage_hdr {
51    uint32_t code[9];
52    uint32_t magic;
53    uint32_t start;
54    uint32_t end;
55};
56
57static int is_uImage(void *file)
58{
59    uint32_t magic = UIMAGE_MAGIC;
60    return memcmp(file, &magic, sizeof(magic));
61}
62
63static int is_zImage(void *file)
64{
65    struct zimage_hdr *hdr;
66    hdr = (struct zimage_hdr *)file;
67    return hdr->magic != ZIMAGE_MAGIC;
68}
69
70static int is_dtb(void *file)
71{
72    struct dtb_hdr *hdr;
73    hdr = (struct dtb_hdr *)file;
74    return hdr->magic != DTB_MAGIC;
75}
76
77static int is_initrd(void *file)
78{
79    /* We currently only support initrd files in the gzip format */
80    struct initrd_gz_hdr *hdr;
81    hdr = (struct initrd_gz_hdr *)file;
82    return hdr->magic != INITRD_GZ_MAGIC;
83}
84
85enum img_type image_get_type(void *file)
86{
87    if (elf_check_magic(file) == 0) {
88        return IMG_ELF;
89    } else if (is_zImage(file) == 0) {
90        return IMG_ZIMAGE;
91    } else if (is_uImage(file) == 0) {
92        return IMG_UIMAGE;
93    } else if (is_dtb(file) == 0) {
94        return IMG_DTB;
95    } else if (is_initrd(file) == 0) {
96        return IMG_INITRD_GZ;
97    } else {
98        return IMG_BIN;
99    }
100}
101
102uintptr_t zImage_get_load_address(void *file, uintptr_t ram_base)
103{
104    struct zimage_hdr *hdr;
105    hdr = (struct zimage_hdr *)file;
106    if (hdr->start == 0) {
107        return ram_base + 0x8000;
108    } else {
109        return hdr->start;
110    }
111    return 0;
112}
113
114static int get_guest_image_type(const char *image_name, enum img_type *image_type, Elf64_Ehdr *header)
115{
116    int fd;
117    uintptr_t load_addr;
118    fd = open(image_name, 0);
119    if (fd == -1) {
120        ZF_LOGE("Error: Unable to open image \'%s\'", image_name);
121        return -1;
122    }
123
124    size_t len = read(fd, header, sizeof(*header));
125    if (len != sizeof(*header)) {
126        ZF_LOGE("Could not read len. File is likely corrupt");
127        close(fd);
128        return -1;
129    }
130    *image_type = image_get_type(header);
131    close(fd);
132    return 0;
133}
134
135static int guest_write_address(vm_t *vm, uintptr_t paddr, void *vaddr, size_t size, size_t offset, void *cookie)
136{
137    memcpy(vaddr, cookie + offset, size);
138    if (config_set(CONFIG_PLAT_TX1) || config_set(CONFIG_PLAT_TX2)) {
139        seL4_CPtr cap = vspace_get_cap(&vm->mem.vmm_vspace, vaddr);
140        if (cap == seL4_CapNull) {
141            /* Not sure how we would get here, something has gone pretty wrong */
142            ZF_LOGE("Failed to get vmm cap for vaddr: %p", vaddr);
143            return -1;
144        }
145        int error = seL4_ARM_Page_CleanInvalidate_Data(cap, 0, PAGE_SIZE_4K);
146        ZF_LOGF_IFERR(error, "seL4_ARM_Page_CleanInvalidate_Data failed");
147    }
148    return 0;
149}
150
151static int load_image(vm_t *vm, const char *image_name, uintptr_t load_addr,  size_t *resulting_image_size)
152{
153    int fd;
154    size_t len;
155    int error;
156    fd = open(image_name, 0);
157    if (fd == -1) {
158        ZF_LOGE("Error: Unable to find image \'%s\'", image_name);
159        return -1;
160    }
161
162    /* Try and load the image 1MiB at a time. Reduce the size by half if
163     * there isn't enough memory available.  The total loading time may be
164     * faster if a larger buffer is used as it allows for more batching.
165     */
166    size_t read_size = BIT(20);
167    char *buf = malloc(read_size);
168    while (buf == NULL) {
169        read_size /= 2;
170        if (read_size < 4096) {
171            ZF_LOGE("Not enough memory for copy buffer");
172        }
173        buf = malloc(read_size);
174    }
175    size_t offset = 0;
176    while (1) {
177        /* Load the image */
178        len = read(fd, buf, read_size);
179        if (!len) {
180            break;
181        }
182        vm_ram_mark_allocated(vm, load_addr + offset, len);
183        error = vm_ram_touch(vm, load_addr + offset, len, guest_write_address, (void *)buf);
184        if (error) {
185            ZF_LOGE("Error: Failed to load \'%s\'", image_name);
186            close(fd);
187            return -1;
188        }
189        offset += len;
190    }
191    free(buf);
192    *resulting_image_size = offset;
193    close(fd);
194    return 0;
195}
196
197static void *load_guest_kernel_image(vm_t *vm, const char *kernel_image_name, uintptr_t load_base_addr,
198                                     size_t *image_size)
199{
200    int err;
201    uintptr_t load_addr;
202    enum img_type ret_file_type;
203    Elf64_Ehdr header = {0};
204    err = get_guest_image_type(kernel_image_name, &ret_file_type, &header);
205    if (err) {
206        return NULL;
207    }
208    /* Determine the load address */
209    switch (ret_file_type) {
210    case IMG_BIN:
211        if (config_set(CONFIG_PLAT_TX1) || config_set(CONFIG_PLAT_TX2) || config_set(CONFIG_PLAT_QEMU_ARM_VIRT)
212            || config_set(CONFIG_PLAT_ODROIDC2)) {
213            /* This is likely an aarch64/aarch32 linux difference */
214            load_addr = load_base_addr + 0x80000;
215        } else {
216            load_addr = load_base_addr + 0x8000;
217        }
218        break;
219    case IMG_ZIMAGE:
220        load_addr = zImage_get_load_address(&header, load_base_addr);
221        break;
222    default:
223        ZF_LOGE("Error: Unknown Linux image format for \'%s\'", kernel_image_name);
224        return NULL;
225    }
226    err = load_image(vm, kernel_image_name, load_addr, image_size);
227    if (err) {
228        return NULL;
229    }
230    return (void *)load_addr;
231}
232
233static void *load_guest_module_image(vm_t *vm, const char *image_name, uintptr_t load_base_addr, size_t *image_size)
234{
235    int err;
236    uintptr_t load_addr;
237    enum img_type ret_file_type;
238    Elf64_Ehdr header = {0};
239    err = get_guest_image_type(image_name, &ret_file_type, &header);
240    if (err) {
241        return NULL;
242    }
243    /* Determine the load address */
244    switch (ret_file_type) {
245    case IMG_DTB:
246    case IMG_INITRD_GZ:
247        load_addr = load_base_addr;
248        break;
249    default:
250        ZF_LOGE("Error: Unknown Linux image format for \'%s\'", image_name);
251        return NULL;
252    }
253    err = load_image(vm, image_name, load_addr, image_size);
254    if (err) {
255        return NULL;
256    }
257    return (void *)load_addr;
258}
259
260int vm_load_guest_kernel(vm_t *vm, const char *kernel_name, uintptr_t load_address, size_t alignment,
261                         guest_kernel_image_t *guest_kernel_image)
262{
263    void *load_addr;
264    size_t kernel_len;
265    if (!guest_kernel_image) {
266        ZF_LOGE("Invalid guest_image_t object");
267        return -1;
268    }
269    load_addr = load_guest_kernel_image(vm, kernel_name, load_address, &kernel_len);
270    if (!load_addr) {
271        return -1;
272    }
273    guest_kernel_image->kernel_image.load_paddr = (uintptr_t)load_addr;
274    guest_kernel_image->kernel_image.size = kernel_len;
275    return 0;
276}
277
278int vm_load_guest_module(vm_t *vm, const char *module_name, uintptr_t load_address, size_t alignment,
279                         guest_image_t *guest_image)
280{
281    void *load_addr;
282    size_t module_len;
283
284    if (!guest_image) {
285        ZF_LOGE("Invalid guest_image_t object");
286        return -1;
287    }
288
289    load_addr = load_guest_module_image(vm, module_name, load_address, &module_len);
290    if (!load_addr) {
291        return -1;
292    }
293
294    guest_image->load_paddr = (uintptr_t)load_addr;
295    guest_image->size = module_len;
296    return 0;
297}
298