aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/ipaccess/ipaccess-firmware.c
blob: 4975b886591f4b1970440b761728e948bd1f9504 (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
/* Routines for parsing an ipacces SDP firmware file */

/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include <openbsc/debug.h>
#include <openbsc/ipaccess.h>
#include <osmocore/talloc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PART_LENGTH 138

static_assert(sizeof(struct sdp_header_entry) == 138, right_entry);
static_assert(sizeof(struct sdp_firmware) == 160, _right_header_length);

/* more magic, the second "int" in the header */
static char more_magic[] = { 0x10, 0x02 };

int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list)
{
	struct sdp_firmware *firmware_header = 0;
	struct sdp_header *header;
	char buf[4096];
	int rc, i;

	rc = read(fd, buf, sizeof(*firmware_header));
	if (rc < 0) {
		perror("Can not read header start.");
		return -1;
	}

	firmware_header = (struct sdp_firmware *) &buf[0];
	if (strncmp(firmware_header->magic, " SDP", 4) != 0) {
		fprintf(stderr, "Wrong magic.\n");
		return -1;
	}

	if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) {
		fprintf(stderr, "Wrong more magic. Got: 0x%x %x %x %x\n",
			firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff,
			firmware_header->more_magic[2] & 0xff, firmware_header->more_magic[3] & 0xff);
		return -1;
	}


	if (ntohl(firmware_header->file_length) != st_size) {
		fprintf(stderr, "The filesize and the header do not match.\n");
		return -1;
	}

	/* add the firmware */
	header = talloc_zero(list, struct sdp_header);
	header->firmware_info = *firmware_header;
	INIT_LLIST_HEAD(&header->header_list);
	llist_add(&header->entry, list);

	if (ntohs(firmware_header->table_offset) != 0) {
		fprintf(stderr, "The table offset is not zero. That is not supported: 0x%x at 0x%x\n",
			ntohs(firmware_header->table_offset), base_offset);
		return -1;
	}


	if (ntohs(firmware_header->part_length) % PART_LENGTH != 0) {
		fprintf(stderr, "The part length seems to be wrong.\n");
		return -1;
	}

	/* look into each firmware now */
	for (i = 0; i < ntohs(firmware_header->part_length) / PART_LENGTH; ++i) {
		struct sdp_header_entry entry;
		struct sdp_header_item *header_entry;
		unsigned int offset = base_offset + sizeof(struct sdp_firmware);
		offset += i * 138;

		if (lseek(fd, offset, SEEK_SET) != offset) {
			fprintf(stderr, "Can not seek to the offset: %u.\n", offset);
			return -1;
		}

		rc = read(fd, &entry, sizeof(entry));
		if (rc != sizeof(entry)) {
			fprintf(stderr, "Can not read the header entry.\n");
			return -1;
		}

		/* now we need to find the SDP file... */
		offset = ntohl(entry.start) + 4 + base_offset;
		if (lseek(fd, offset, SEEK_SET) != offset) {
			perror("can't seek to sdp");
			return -1;
		}

		header_entry = talloc_zero(header,  struct sdp_header_item);
		header_entry->header_entry = entry;
		llist_add(&header_entry->entry, &header->header_list);

		ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
	}

	return 0;
}