1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (c) Copyright 2016 by VRT Technology
4 *
5 * Author:
6 *  Stuart Longland <stuartl@vrt.com.au>
7 *
8 * Based on FAT environment driver
9 * (c) Copyright 2011 by Tigris Elektronik GmbH
10 *
11 * Author:
12 *  Maximilian Schwerin <mvs@tigris.de>
13 *
14 * and EXT4 filesystem implementation
15 * (C) Copyright 2011 - 2012 Samsung Electronics
16 * EXT4 filesystem implementation in Uboot by
17 * Uma Shankar <uma.shankar@samsung.com>
18 * Manjunatha C Achar <a.manjunatha@samsung.com>
19 */
20
21#include <common.h>
22#include <part.h>
23
24#include <command.h>
25#include <env.h>
26#include <env_internal.h>
27#include <linux/stddef.h>
28#include <malloc.h>
29#include <memalign.h>
30#include <search.h>
31#include <errno.h>
32#include <ext4fs.h>
33#include <mmc.h>
34#include <scsi.h>
35#include <virtio.h>
36#include <asm/global_data.h>
37
38DECLARE_GLOBAL_DATA_PTR;
39
40__weak const char *env_ext4_get_intf(void)
41{
42	return (const char *)CONFIG_ENV_EXT4_INTERFACE;
43}
44
45__weak const char *env_ext4_get_dev_part(void)
46{
47#ifdef CONFIG_MMC
48	static char *part_str;
49
50	if (!part_str) {
51		part_str = CONFIG_ENV_EXT4_DEVICE_AND_PART;
52		if (!strcmp(CONFIG_ENV_EXT4_INTERFACE, "mmc") && part_str[0] == ':') {
53			part_str = "0" CONFIG_ENV_EXT4_DEVICE_AND_PART;
54			part_str[0] += mmc_get_env_dev();
55		}
56	}
57
58	return part_str;
59#else
60	return (const char *)CONFIG_ENV_EXT4_DEVICE_AND_PART;
61#endif
62}
63
64static int env_ext4_save_buffer(env_t *env_new)
65{
66	struct blk_desc *dev_desc = NULL;
67	struct disk_partition info;
68	int dev, part;
69	int err;
70	const char *ifname = env_ext4_get_intf();
71	const char *dev_and_part = env_ext4_get_dev_part();
72
73	part = blk_get_device_part_str(ifname, dev_and_part,
74				       &dev_desc, &info, 1);
75	if (part < 0)
76		return 1;
77
78	dev = dev_desc->devnum;
79	ext4fs_set_blk_dev(dev_desc, &info);
80
81	if (!ext4fs_mount()) {
82		printf("\n** Unable to use %s %s for saveenv **\n",
83		       ifname, dev_and_part);
84		return 1;
85	}
86
87	err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)env_new,
88			   sizeof(env_t), FILETYPE_REG);
89	ext4fs_close();
90
91	if (err == -1) {
92		printf("\n** Unable to write \"%s\" from %s%d:%d **\n",
93			CONFIG_ENV_EXT4_FILE, ifname, dev, part);
94		return 1;
95	}
96
97	return 0;
98}
99
100static int env_ext4_save(void)
101{
102	env_t env_new;
103	int err;
104
105	err = env_export(&env_new);
106	if (err)
107		return err;
108
109	err = env_ext4_save_buffer(&env_new);
110	if (err)
111		return err;
112
113	gd->env_valid = ENV_VALID;
114	puts("done\n");
115
116	return 0;
117}
118
119static int env_ext4_erase(void)
120{
121	env_t env_new;
122	int err;
123
124	memset(&env_new, 0, sizeof(env_t));
125
126	err = env_ext4_save_buffer(&env_new);
127	if (err)
128		return err;
129
130	gd->env_valid = ENV_INVALID;
131	puts("done\n");
132
133	return 0;
134}
135
136static int env_ext4_load(void)
137{
138	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
139	struct blk_desc *dev_desc = NULL;
140	struct disk_partition info;
141	int dev, part;
142	int err;
143	loff_t off;
144	const char *ifname = env_ext4_get_intf();
145	const char *dev_and_part = env_ext4_get_dev_part();
146
147#ifdef CONFIG_MMC
148	if (!strcmp(ifname, "mmc"))
149		mmc_initialize(NULL);
150#endif
151#if defined(CONFIG_AHCI) || defined(CONFIG_SCSI)
152	if (!strcmp(ifname, "scsi"))
153		scsi_scan(true);
154#endif
155#if defined(CONFIG_VIRTIO)
156	if (!strcmp(ifname, "virtio"))
157		virtio_init();
158#endif
159
160	part = blk_get_device_part_str(ifname, dev_and_part,
161				       &dev_desc, &info, 1);
162	if (part < 0)
163		goto err_env_relocate;
164
165	dev = dev_desc->devnum;
166	ext4fs_set_blk_dev(dev_desc, &info);
167
168	if (!ext4fs_mount()) {
169		printf("\n** Unable to use %s %s for loading the env **\n",
170		       ifname, dev_and_part);
171		goto err_env_relocate;
172	}
173
174	err = ext4_read_file(CONFIG_ENV_EXT4_FILE, buf, 0, CONFIG_ENV_SIZE,
175			     &off);
176	ext4fs_close();
177
178	if (err == -1) {
179		printf("\n** Unable to read \"%s\" from %s%d:%d **\n",
180			CONFIG_ENV_EXT4_FILE, ifname, dev, part);
181		goto err_env_relocate;
182	}
183
184	err = env_import(buf, 1, H_EXTERNAL);
185	if (!err)
186		gd->env_valid = ENV_VALID;
187
188	return err;
189
190err_env_relocate:
191	env_set_default(NULL, 0);
192
193	return -EIO;
194}
195
196U_BOOT_ENV_LOCATION(ext4) = {
197	.location	= ENVL_EXT4,
198	ENV_NAME("EXT4")
199	.load		= env_ext4_load,
200	.save		= ENV_SAVE_PTR(env_ext4_save),
201	.erase		= ENV_ERASE_PTR(env_ext4_erase),
202};
203