summaryrefslogtreecommitdiffstats
path: root/payloads/bayou/self.c
blob: 04db3f672fc6261bc1ea30c8a8b291b674469236 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * This file is part of the bayou project.
 *
 * Copyright (C) 2008 Advanced Micro Devices, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc.
 */

#include "bayou.h"
#include "self.h"

static int nop_decompress(void *dst, void *src, int len)
{
	memcpy(dst, src, len);
	return len;
}

#ifdef CONFIG_LZMA
extern int ulzma(u8 *, u8 *);

static int lzma_decompress(void *dst, void *src, int len)
{
	return ulzma((u8 *) src, (u8 *) dst);
}
#endif

#ifdef CONFIG_NRV2B
extern int unrv2b(u8 *, u8 *, unsigned long *);

static int nrv2b_decompress(void *dst, void *src, int len)
{
	unsigned long l = (u32) len;
	return unrv2b(src, dst, &l);
}
#endif

static int zeros_decompress(void *dst, void *src, int len)
{
	memset(dst, 0, len);
	return len;
}

int self_get_params(u8 *fptr, u8 **params)
{
	struct self_segment *seg = (struct self_segment *)fptr;

	while (seg->type != SELF_TYPE_ENTRY) {
		if (seg->type == 0)
			return -1;

		if (seg->type == SELF_TYPE_PARAMS) {
			*params = (u8 *) (fptr + seg->offset);
			return seg->len;
		}

		seg++;
	}

	*params = NULL;

	return 0;
}

int verify_self(u8 *ptr)
{
	struct self_segment *seg = (struct self_segment *)ptr;

	switch (seg->type) {
	case SELF_TYPE_CODE:
	case SELF_TYPE_DATA:
	case SELF_TYPE_BSS:
	case SELF_TYPE_PARAMS:
	case SELF_TYPE_ENTRY:
		return 1;
	}

	return 0;
}

int self_load_and_run(struct payload *p, int *ret)
{
	struct self_segment *seg = (struct self_segment *)p->fptr;
	int (*dcmp) (void *, void *, int);
	int dlen;

	switch (p->stat.compression) {
#ifdef CONFIG_LZMA
	case ALGO_LZMA:
		dcmp = lzma_decompress;
		break;
#endif
#ifdef CONFIG_NRV2B
	case ALGO_NRV2B:
		dcmp = nrv2b_decompress;
		break;
#endif
	case ALGO_ZEROES:
		dcmp = zeros_decompress;
		break;
	case ALGO_NONE:
		dcmp = nop_decompress;
	default:
		printf("E: Unsupported decompression type\n");
		return -1;
	}

	while (1) {
		u32 laddr = (u32) (seg->load_addr & 0xFFFFFFFF);

		switch (seg->type) {
		case SELF_TYPE_CODE:
		case SELF_TYPE_DATA:
			dlen = dcmp((void *)laddr,
				    (void *)p->fptr + seg->offset, seg->len);

			if (dlen < seg->mem_len) {
				memset((void *)(laddr + dlen), 0,
				       seg->mem_len - dlen);
			}
			break;

		case SELF_TYPE_BSS:
			memset((void *)laddr, 0, seg->len);
			break;
		case SELF_TYPE_ENTRY:
			*ret = exec(laddr, 0, NULL);
			return 0;
		default:
			break;
		}

		seg++;
	}

	return -1;
}