1 /*
2  * mbr.c
3  * (C) Copyright 2012
4  * Patrick H Wood, All rights reserved.
5  * Heavily modified from the Allwinner file drivers/block/sun4i_nand/nfd/mbr.c.
6  * (Allwinner copyright block retained below.)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  *
23  */
24 
25 /*
26  * drivers/block/sun4i_nand/nfd/mbr.c
27  * (C) Copyright 2007-2012
28  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
29  *
30  * This program is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU General Public License as
32  * published by the Free Software Foundation; either version 2 of
33  * the License, or (at your option) any later version.
34  *
35  * This program is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License
41  * along with this program; if not, write to the Free Software
42  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
43  * MA 02111-1307 USA
44  */
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #ifdef __linux__
54 # include <sys/ioctl.h>
55 # include <sys/mount.h> /* BLKRRPART */
56 #endif
57 #include "nand-common.h"
58 
59 // so far, only known formats are for A10 and A20
60 #if defined(A10)
61 # include "nand-part-a10.h"
62 #elif defined(A20)
63 # include "nand-part-a20.h"
64 #endif
65 
66 #define MAX_NAME 16
67 
printmbrheader(MBR * mbr)68 static void printmbrheader(MBR *mbr)
69 {
70 	printf("mbr: version 0x%08x, magic %8.8s\n", mbr->version, mbr->magic);
71 }
72 
_get_mbr(int fd,int mbr_num,int force)73 static MBR *_get_mbr(int fd, int mbr_num, int force)
74 {
75 	MBR *mbr;
76 
77 	/*request mbr space*/
78 	mbr = malloc(sizeof(MBR));
79 	if(mbr == NULL)
80 	{
81 		printf("%s : request memory fail\n",__FUNCTION__);
82 		return NULL;
83 	}
84 
85 	/*get mbr from nand device*/
86 	lseek(fd,MBR_START_ADDRESS + MBR_SIZE*mbr_num,SEEK_SET);
87 	if(read(fd,mbr,MBR_SIZE) == MBR_SIZE)
88 	{
89 		/*checksum*/
90 		printf("check partition table copy %d: ", mbr_num);
91 		printmbrheader(mbr);
92 		if (force) {
93 			strncpy((char *)mbr->magic, MBR_MAGIC, 8);
94 			mbr->version = MBR_VERSION;
95 			return mbr;
96 		}
97 		if(strncmp((char *)mbr->magic, MBR_MAGIC, 8))
98 		{
99 			printf("magic %8.8s is not %8s\n", mbr->magic, MBR_MAGIC);
100 			return NULL;
101 		}
102 		if(mbr->version != MBR_VERSION)
103 		{
104 			printf("version 0x%08x is not 0x%08x\n", mbr->version, MBR_VERSION);
105 			return NULL;
106 		}
107 		if(*(__u32 *)mbr == calc_crc32((__u32 *)mbr + 1,MBR_SIZE - 4))
108 		{
109 			printf("OK\n");
110 			return mbr;
111 		}
112 		printf("BAD!\n");
113 	}
114 	return NULL;
115 }
116 
_free_mbr(MBR * mbr)117 static __s32 _free_mbr(MBR *mbr)
118 {
119 	if(mbr)
120 	{
121 		free(mbr);
122 		mbr = 0;
123 	}
124 
125 	return 0;
126 }
127 
printmbr(MBR * mbr)128 static void printmbr(MBR *mbr)
129 {
130 	unsigned int part_cnt;
131 
132 	printmbrheader(mbr);
133 	printf("%d partitions\n", mbr->PartCount);
134 	for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++)
135 	{
136 		printf("partition %2d: class = %12s, name = %12s, partition start = %8d, partition size = %8d user_type=%d\n",
137 					part_cnt + 1,
138 					mbr->array[part_cnt].classname,
139 					mbr->array[part_cnt].name,
140 					mbr->array[part_cnt].addrlo,
141 					mbr->array[part_cnt].lenlo,
142 					mbr->array[part_cnt].user_type);
143 	}
144 }
checkmbrs(int fd)145 int checkmbrs(int fd)
146 {
147 	int i;
148 	MBR *mbrs[MBR_COPY_NUM];
149 	MBR *mbr = NULL;
150 
151 	memset((void *) mbrs, 0, sizeof(mbrs));
152 	for (i = 0; i < MBR_COPY_NUM; i++) {
153 		mbrs[i] = _get_mbr(fd, i, 0);
154 		if (mbrs[i])
155 			mbr = mbrs[i];
156 	}
157 	if (!mbr) {
158 		printf("all partition tables are bad!\n");
159 		for (i = 0; i < MBR_COPY_NUM; i++) {
160 			if (mbrs[i])
161 				_free_mbr(mbrs[i]);
162 		}
163 		return 0;
164 	}
165 
166 	printmbr(mbr);
167 	for (i = 0; i < MBR_COPY_NUM; i++) {
168 		if (mbrs[i])
169 			_free_mbr(mbrs[i]);
170 	}
171 	return 1;
172 }
173 
writembrs(int fd,char names[][MAX_NAME],__u32 start,__u32 * lens,unsigned int * user_types,int nparts,int partoffset,int force)174 static int writembrs(int fd, char names[][MAX_NAME], __u32 start, __u32 *lens, unsigned int *user_types, int nparts, int partoffset, int force)
175 {
176 	unsigned int part_cnt = 0;
177 	int i;
178 	char yn = 'n';
179 	MBR *mbrs[MBR_COPY_NUM];
180 	MBR *mbr = NULL;
181 	FILE *backup;
182 
183 	memset((void *) mbrs, 0, sizeof(mbrs));
184 	for (i = 0; i < MBR_COPY_NUM; i++) {
185 		mbrs[i] = _get_mbr(fd, i, force);
186 		if (mbrs[i])
187 			mbr = mbrs[i];
188 	}
189 	if (!mbr) {
190 		printf("all partition tables are bad!\n");
191 		for (i = 0; i < MBR_COPY_NUM; i++) {
192 			if (mbrs[i])
193 				_free_mbr(mbrs[i]);
194 		}
195 		return 0;
196 	}
197 	// back up mbr data
198 	backup = fopen("nand_mbr.backup", "w");
199 	if (!backup) {
200 		printf("can't open nand_mbr.backup to back up mbr data\n");
201 		for (i = 0; i < MBR_COPY_NUM; i++) {
202 			if (mbrs[i])
203 				_free_mbr(mbrs[i]);
204 		}
205 		return 0;
206 	}
207 
208 	fprintf(backup, "%d ", mbr->array[0].addrlo);
209 	for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++)
210 	{
211 		fprintf(backup, "'%s %d %d' ", mbr->array[part_cnt].name,
212 		                  mbr->array[part_cnt].lenlo, mbr->array[part_cnt].user_type);
213 	}
214 	fprintf(backup, "\n");
215 	fclose(backup);
216 
217 	mbr->PartCount = nparts + partoffset;
218 	if (partoffset)
219 		start = mbr->array[0].addrlo + mbr->array[0].lenlo;
220 	for(i = 0; i < nparts; i++) {
221 		strcpy((char *)mbr->array[i+partoffset].name, names[i]);
222 		strcpy((char *)mbr->array[i+partoffset].classname, "DISK");
223 		memset((void *) mbr->array[i+partoffset].res, 0, sizeof(mbr->array[i+partoffset].res));
224 		mbr->array[i+partoffset].user_type = user_types[i];
225 		mbr->array[i+partoffset].ro = 0;
226 		mbr->array[i+partoffset].addrhi = 0;
227 		mbr->array[i+partoffset].lenhi = 0;
228 		mbr->array[i+partoffset].addrlo = start;
229 		mbr->array[i+partoffset].lenlo = lens[i];
230 		start += lens[i];
231 	}
232 
233 	printf("\nready to write new partition tables:\n");
234 	printmbr(mbr);
235 	for (i = 0; i < MBR_COPY_NUM; i++) {
236 		if (mbrs[i])
237 			_free_mbr(mbrs[i]);
238 	}
239 	printf("\nwrite new partition tables? (Y/N)\n");
240 	read(0, &yn, 1);
241 	if (yn != 'Y' && yn != 'y') {
242 		printf("aborting\n");
243 		return 0;
244 	}
245 
246 	for (i = 0; i < MBR_COPY_NUM; i++) {
247 		mbr->index = i;
248 		// calculate new checksum
249 		*(__u32 *)mbr = calc_crc32((__u32 *)mbr + 1,MBR_SIZE - 4);
250 		lseek(fd,MBR_START_ADDRESS + MBR_SIZE*i,SEEK_SET);
251 		write(fd,mbr,MBR_SIZE);
252 	}
253 
254 #ifdef __linux__
255 	if (ioctl(fd, BLKRRPART, NULL))
256 		perror("Failed rereading partition table");
257 #endif
258 
259 	return 1;
260 }
261 
nand_part(int argc,char ** argv,const char * cmd,int fd,int force)262 int nand_part (int argc, char **argv, const char *cmd, int fd, int force)
263 {
264 	int partoffset = 0;
265 	int i;
266 	char names[MAX_PART_COUNT][MAX_NAME];
267 	__u32 lens[MAX_PART_COUNT];
268 	unsigned int user_types[MAX_PART_COUNT];
269 	__u32 start;
270 
271 
272 	// parse name/len arguments
273 	memset((void *) user_types, 0, sizeof(user_types));
274 	if (argc > 0) {
275 		if (sscanf(argv[0], "%u", &start) != 1) {
276 			partoffset++;
277 			if (force) {
278 				printf("if using -f, must set info for first partition\n");
279 				usage(cmd);
280 				close(fd);
281 				return -3;
282 			}
283 		}
284 		else {
285 			argc--;
286 			argv++;
287 		}
288 
289 		if (start < MBR_SIZE * MBR_COPY_NUM / 512) {
290 			printf("Partition 1 starting offset must be at least %d\n", MBR_SIZE * MBR_COPY_NUM / 512);
291 			close(fd);
292 			return -3;
293 		}
294 
295 		for (i = 0; i < argc; i++) {
296 			if (sscanf(argv[i], "%s %d %d", names[i], &lens[i], &user_types[i]) < 2) {
297 				printf("bad 'name len' argument\n");
298 				usage(cmd);
299 				close(fd);
300 				return -3;
301 			}
302 		}
303 	}
304 
305 	checkmbrs(fd);
306 
307 	if (argc > MAX_PART_COUNT - partoffset) {
308 		printf("too many partitions specified (MAX 14)\n");
309 		usage(cmd);
310 		close(fd);
311 		return -2;
312 	}
313 
314 
315 	if (argc > 0) {
316 		if (writembrs(fd, names, start, lens, user_types, argc, partoffset, force)) {
317 			printf("\nverifying new partition tables:\n");
318 			checkmbrs(fd);
319 #ifdef __linux__
320 			printf("rereading partition table... returned %d\n", ioctl(fd, BLKRRPART, 0));
321 #endif
322 		}
323 	}
324 	close(fd);
325 
326 	return 0;
327 }
328