summaryrefslogtreecommitdiffstats
path: root/kexec/kexec-pe-zboot.c
blob: 3abd17d9fe5975ff7d08b5a667daf4c63ba44403 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Generic PE compressed Image (vmlinuz, ZBOOT) support.
 *
 * Several distros use 'make zinstall' with CONFIG_ZBOOT
 * enabled to create UEFI PE images that contain
 * a decompressor and a compressed kernel image.
 *
 * Currently we cannot use kexec_file_load() to load vmlinuz
 * PE images that self decompress.
 *
 * To support ZBOOT, we should:
 * a). Copy the compressed contents of vmlinuz to a temporary file.
 * b). Decompress (gunzip-decompress) the contents inside the
 *     temporary file.
 * c). Validate the resulting image and write it back to the
 *     temporary file.
 * d). Pass the 'fd' of the temporary file to the kernel space.
 *
 * This module contains the arch independent code for the above,
 * arch specific PE and image checks should wrap calls
 * to functions in this module.
 */

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include "kexec.h"
#include <kexec-pe-zboot.h>

#define FILENAME_IMAGE		"/tmp/ImageXXXXXX"

/*
 * Returns -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format.
 *
 * crude_buf: the content, which is read from the kernel file without any processing
 */
int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd,
		off_t *kernel_size)
{
	int ret = -1;
	int fd = 0;
	char *fname = NULL;
	char *kernel_uncompressed_buf = NULL;
	off_t decompressed_size = 0;
	const struct linux_pe_zboot_header *z;

	z = (const struct linux_pe_zboot_header *)(crude_buf);

	if (memcmp(&z->image_type, "zimg", sizeof(z->image_type))) {
		dbgprintf("%s: PE doesn't contain a compressed kernel.\n", __func__);
		return -1;
	}

	/*
	 * At the moment its possible to create images with more compression
	 * algorithms than are supported here, error out if we detect that.
	 */
	if (memcmp(&z->compress_type, "gzip", 4) &&
	    memcmp(&z->compress_type, "lzma", 4)) {
		dbgprintf("%s: kexec can only decompress gziped and lzma images.\n", __func__);
		return -1;
	}

	if (buf_sz < z->payload_offset + z->payload_size) {
		dbgprintf("%s: PE too small to contain complete payload.\n", __func__);
		return -1;
	}

	if (!(fname = strdup(FILENAME_IMAGE))) {
		dbgprintf("%s: Can't duplicate strings\n", __func__);
		return -1;
	}

	if ((fd = mkstemp(fname)) < 0) {
		dbgprintf("%s: Can't open file %s\n", __func__,	fname);
		ret = -1;
		goto fail_mkstemp;
	}

	if (write(fd, &crude_buf[z->payload_offset],
		  z->payload_size) != z->payload_size) {
		dbgprintf("%s: Can't write the compressed file %s\n",
				__func__, fname);
		ret = -1;
		goto fail_write;
	}

	kernel_uncompressed_buf = slurp_decompress_file(fname,
							&decompressed_size);

	dbgprintf("%s: decompressed size %ld\n", __func__, decompressed_size);

	lseek(fd, 0, SEEK_SET);

	if (write(fd,  kernel_uncompressed_buf,
		  decompressed_size) != decompressed_size) {
		dbgprintf("%s: Can't write the decompressed file %s\n",
				__func__, fname);
		ret = -1;
		goto fail_bad_header;
	}

	*kernel_fd = open(fname, O_RDONLY);
	if (*kernel_fd == -1) {
		dbgprintf("%s: Failed to open file %s\n",
				__func__, fname);
		ret = -1;
		goto fail_bad_header;
	}

	*kernel_size = decompressed_size;
	dbgprintf("%s: done\n", __func__);

	ret = 0;
	goto fail_write;

fail_bad_header:
	free(kernel_uncompressed_buf);

fail_write:
	if (fd >= 0)
		close(fd);

	unlink(fname);

fail_mkstemp:
	free(fname);

	return ret;
}