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 §ors, &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