summaryrefslogtreecommitdiffstats
path: root/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
blob: d206fecd5b197454a79babfbf770644b54da0bde (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
/*
 * Copyright (c) 2015 The Linux Foundation
 * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/types.h>
#include <linux/byteorder/generic.h>
#include <linux/slab.h>
#include <linux/of_fdt.h>

#include "mtdsplit.h"

struct fdt_header {
	uint32_t magic;			 /* magic word FDT_MAGIC */
	uint32_t totalsize;		 /* total size of DT block */
	uint32_t off_dt_struct;		 /* offset to structure */
	uint32_t off_dt_strings;	 /* offset to strings */
	uint32_t off_mem_rsvmap;	 /* offset to memory reserve map */
	uint32_t version;		 /* format version */
	uint32_t last_comp_version;	 /* last compatible version */

	/* version 2 fields below */
	uint32_t boot_cpuid_phys;	 /* Which physical CPU id we're
					    booting on */
	/* version 3 fields below */
	uint32_t size_dt_strings;	 /* size of the strings block */

	/* version 17 fields below */
	uint32_t size_dt_struct;	 /* size of the structure block */
};

static int
mtdsplit_fit_parse(struct mtd_info *mtd,
		   const struct mtd_partition **pparts,
	           struct mtd_part_parser_data *data)
{
	struct fdt_header hdr;
	size_t hdr_len, retlen;
	size_t offset;
	size_t fit_offset, fit_size;
	size_t rootfs_offset, rootfs_size;
	struct mtd_partition *parts;
	int ret;

	hdr_len = sizeof(struct fdt_header);

	/* Parse the MTD device & search for the FIT image location */
	for(offset = 0; offset + hdr_len < mtd->size; offset += mtd->erasesize) {
		ret = mtd_read(mtd, offset, hdr_len, &retlen, (void*) &hdr);
		if (ret) {
			pr_err("read error in \"%s\" at offset 0x%llx\n",
			       mtd->name, (unsigned long long) offset);
			return ret;
		}

		if (retlen != hdr_len) {
			pr_err("short read in \"%s\"\n", mtd->name);
			return -EIO;
		}

		/* Check the magic - see if this is a FIT image */
		if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
			pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
				 mtd->name, (unsigned long long) offset);
			continue;
		}

		/* We found a FIT image. Let's keep going */
		break;
	}

	fit_offset = offset;
	fit_size = be32_to_cpu(hdr.totalsize);

	if (fit_size == 0) {
		pr_err("FIT image in \"%s\" at offset %llx has null size\n",
		       mtd->name, (unsigned long long) fit_offset);
		return -ENODEV;
	}

	/* Search for the rootfs partition after the FIT image */
	ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
				   &rootfs_offset, NULL);
	if (ret) {
		pr_info("no rootfs found after FIT image in \"%s\"\n",
			mtd->name);
		return ret;
	}

	rootfs_size = mtd->size - rootfs_offset;

	parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
	if (!parts)
		return -ENOMEM;

	parts[0].name = KERNEL_PART_NAME;
	parts[0].offset = fit_offset;
	parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;

	parts[1].name = ROOTFS_PART_NAME;
	parts[1].offset = rootfs_offset;
	parts[1].size = rootfs_size;

	*pparts = parts;
	return 2;
}

static const struct of_device_id mtdsplit_fit_of_match_table[] = {
	{ .compatible = "denx,fit" },
	{},
};

static struct mtd_part_parser uimage_parser = {
	.owner = THIS_MODULE,
	.name = "fit-fw",
	.of_match_table = mtdsplit_fit_of_match_table,
	.parse_fn = mtdsplit_fit_parse,
	.type = MTD_PARSER_TYPE_FIRMWARE,
};

/**************************************************
 * Init
 **************************************************/

static int __init mtdsplit_fit_init(void)
{
	register_mtd_parser(&uimage_parser);

	return 0;
}

module_init(mtdsplit_fit_init);