1 /*
2  * Helper functions for reading the memory map of a device
3  * following the ST DfuSe 1.1a specification.
4  *
5  * Copyright 2011-2014 Tormod Volden <debian.tormod@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 
31 #include "portable.h"
32 #include "dfu_file.h"
33 #include "dfuse_mem.h"
34 
add_segment(struct memsegment ** segment_list,struct memsegment segment)35 int add_segment(struct memsegment **segment_list, struct memsegment segment)
36 {
37 	struct memsegment *new_element;
38 
39 	new_element = dfu_malloc(sizeof(struct memsegment));
40 	*new_element = segment;
41 	new_element->next = NULL;
42 
43 	if (*segment_list == NULL)
44 		/* list can be empty on first call */
45 		*segment_list = new_element;
46 	else {
47 		struct memsegment *next_element;
48 
49 		/* find last element in list */
50 		next_element = *segment_list;
51 		while (next_element->next != NULL)
52 			next_element = next_element->next;
53 		next_element->next = new_element;
54 	}
55 	return 0;
56 }
57 
find_segment(struct memsegment * segment_list,unsigned int address)58 struct memsegment *find_segment(struct memsegment *segment_list,
59 				unsigned int address)
60 {
61 	while (segment_list != NULL) {
62 		if (segment_list->start <= address &&
63 		    segment_list->end >= address)
64 			return segment_list;
65 		segment_list = segment_list->next;
66 	}
67 	return NULL;
68 }
69 
free_segment_list(struct memsegment * segment_list)70 void free_segment_list(struct memsegment *segment_list)
71 {
72 	struct memsegment *next_element;
73 
74 	while (segment_list->next != NULL) {
75 		next_element = segment_list->next;
76 		free(segment_list);
77 		segment_list = next_element;
78 	}
79 	free(segment_list);
80 }
81 
82 /* Parse memory map from interface descriptor string
83  * encoded as per ST document UM0424 section 4.3.2.
84  */
parse_memory_layout(char * intf_desc)85 struct memsegment *parse_memory_layout(char *intf_desc)
86 {
87 
88 	char multiplier, memtype;
89 	unsigned int address;
90 	int sectors, size;
91 	char *name, *typestring;
92 	int ret;
93 	int count = 0;
94 	char separator;
95 	int scanned;
96 	struct memsegment *segment_list = NULL;
97 	struct memsegment segment;
98 
99 	name = dfu_malloc(strlen(intf_desc));
100 
101 	ret = sscanf(intf_desc, "@%[^/]%n", name, &scanned);
102 	if (ret < 1) {
103 		free(name);
104 		warnx("Could not read name, sscanf returned %d", ret);
105 		return NULL;
106 	}
107 	printf("DfuSe interface name: \"%s\"\n", name);
108 
109 	intf_desc += scanned;
110 	typestring = dfu_malloc(strlen(intf_desc));
111 
112 	while (ret = sscanf(intf_desc, "/0x%x/%n", &address, &scanned),
113 	       ret > 0) {
114 
115 		intf_desc += scanned;
116 		while (ret = sscanf(intf_desc, "%d*%d%c%[^,/]%n",
117 				    &sectors, &size, &multiplier, typestring,
118 				    &scanned), ret > 2) {
119 			intf_desc += scanned;
120 
121 			count++;
122 			memtype = 0;
123 			if (ret == 4) {
124 				if (strlen(typestring) == 1
125 				    && typestring[0] != '/')
126 					memtype = typestring[0];
127 				else {
128 					warnx("Parsing type identifier '%s' "
129 						"failed for segment %i",
130 						typestring, count);
131 					continue;
132 				}
133 			}
134 
135 			/* Quirk for STM32F4 devices */
136 			if (strcmp(name, "Device Feature") == 0)
137 				memtype = 'e';
138 
139 			switch (multiplier) {
140 			case 'B':
141 				break;
142 			case 'K':
143 				size *= 1024;
144 				break;
145 			case 'M':
146 				size *= 1024 * 1024;
147 				break;
148 			case 'a':
149 			case 'b':
150 			case 'c':
151 			case 'd':
152 			case 'e':
153 			case 'f':
154 			case 'g':
155 				if (!memtype) {
156 					warnx("Non-valid multiplier '%c', "
157 						"interpreted as type "
158 						"identifier instead",
159 						multiplier);
160 					memtype = multiplier;
161 					break;
162 				}
163 				/* if memtype was already set: */
164 				/* fall-through */
165 			default:
166 				warnx("Non-valid multiplier '%c', "
167 					"assuming bytes", multiplier);
168 			}
169 
170 			if (!memtype) {
171 				warnx("No valid type for segment %d\n", count);
172 				continue;
173 			}
174 
175 			segment.start = address;
176 			segment.end = address + sectors * size - 1;
177 			segment.pagesize = size;
178 			segment.memtype = memtype & 7;
179 			add_segment(&segment_list, segment);
180 
181 			if (verbose)
182 				printf("Memory segment at 0x%08x %3d x %4d = "
183 				       "%5d (%s%s%s)\n",
184 				       address, sectors, size, sectors * size,
185 				       memtype & DFUSE_READABLE  ? "r" : "",
186 				       memtype & DFUSE_ERASABLE  ? "e" : "",
187 				       memtype & DFUSE_WRITEABLE ? "w" : "");
188 
189 			address += sectors * size;
190 
191 			separator = *intf_desc;
192 			if (separator == ',')
193 				intf_desc += 1;
194 			else
195 				break;
196 		}	/* while per segment */
197 
198 	}		/* while per address */
199 	free(name);
200 	free(typestring);
201 
202 	return segment_list;
203 }
204