1 /* Copyright 2013-2015 IBM Corp.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 * implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <limits.h>
21 #include <unistd.h>
22 #include <dirent.h>
23 #include <stdbool.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <string.h>
28
29 #include <libflash/file.h>
30
31 #include "arch_flash.h"
32
33 #define FDT_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash"
34 #define SYSFS_MTD_PATH "/sys/class/mtd"
35
hint_root(void)36 static inline void hint_root(void)
37 {
38 fprintf(stderr, "Do you have permission? Are you root?\n");
39 }
40
get_dev_attr(const char * dev,const char * attr_file,uint32_t * attr)41 static int get_dev_attr(const char *dev, const char *attr_file, uint32_t *attr)
42 {
43 char *dev_path = NULL;
44 int fd, rc;
45
46 /*
47 * Needs to be large enough to hold at most uint32_t represented as a
48 * string in hex with leading 0x
49 */
50 char attr_buf[10];
51
52 rc = asprintf(&dev_path, "%s/%s/%s", SYSFS_MTD_PATH, dev, attr_file);
53 if (rc < 0) {
54 dev_path = NULL;
55 goto out;
56 }
57
58 fd = open(dev_path, O_RDONLY);
59 if (fd == -1)
60 goto out;
61
62 rc = read(fd, attr_buf, sizeof(attr_buf));
63 close(fd);
64 if (rc == -1)
65 goto out;
66
67 if (attr)
68 *attr = strtol(attr_buf, NULL, 0);
69
70 free(dev_path);
71 return 0;
72
73 out:
74 free(dev_path);
75 fprintf(stderr, "Couldn't get MTD attribute '%s' from '%s'\n", dev, attr_file);
76 return -1;
77 }
78
get_dev_mtd(const char * fdt_flash_path,char ** mtd_path)79 static int get_dev_mtd(const char *fdt_flash_path, char **mtd_path)
80 {
81 struct dirent **namelist;
82 char fdt_node_path[PATH_MAX];
83 int count, i, rc, fd;
84 bool done;
85
86 if (!fdt_flash_path)
87 return -1;
88
89 fd = open(fdt_flash_path, O_RDONLY);
90 if (fd == -1) {
91 fprintf(stderr, "Couldn't open '%s' FDT attribute to determine which flash device to use\n",
92 fdt_flash_path);
93 fprintf(stderr, "Is your skiboot new enough to expose the flash through the device tree?\n");
94 hint_root();
95 return -1;
96 }
97
98 rc = read(fd, fdt_node_path, sizeof(fdt_node_path));
99 close(fd);
100 if (rc == -1) {
101 fprintf(stderr, "Couldn't read flash FDT node from '%s'\n", fdt_flash_path);
102 hint_root();
103 return -1;
104 }
105
106 count = scandir(SYSFS_MTD_PATH, &namelist, NULL, alphasort);
107 if (count == -1) {
108 fprintf(stderr, "Couldn't scan '%s' for MTD\n", SYSFS_MTD_PATH);
109 hint_root();
110 return -1;
111 }
112
113 rc = 0;
114 done = false;
115 for (i = 0; i < count; i++) {
116 struct dirent *dirent;
117 char *dev_path;
118 char fdt_node_path_tmp[PATH_MAX];
119
120 dirent = namelist[i];
121
122 /*
123 * The call to asprintf must happen last as when it succeeds it
124 * will allocate dev_path
125 */
126 if (dirent->d_name[0] == '.' || rc || done ||
127 asprintf(&dev_path, "%s/%s/device/of_node", SYSFS_MTD_PATH, dirent->d_name) < 0) {
128 free(namelist[i]);
129 continue;
130 }
131
132 rc = readlink(dev_path, fdt_node_path_tmp, sizeof(fdt_node_path_tmp) - 1);
133 free(dev_path);
134 if (rc == -1) {
135 /*
136 * This might fail because it could not exist if the system has flash
137 * devices that present as mtd but don't have corresponding FDT
138 * nodes, just continue silently.
139 */
140 free(namelist[i]);
141 /* Should still try the next dir so reset rc */
142 rc = 0;
143 continue;
144 }
145 fdt_node_path_tmp[rc] = '\0';
146
147 if (strstr(fdt_node_path_tmp, fdt_node_path)) {
148 uint32_t flags, size;
149
150 /*
151 * size and flags could perhaps have be gotten another way but this
152 * method is super unlikely to fail so it will do.
153 */
154
155 /* Check to see if device is writeable */
156 rc = get_dev_attr(dirent->d_name, "flags", &flags);
157 if (rc) {
158 free(namelist[i]);
159 continue;
160 }
161
162 /* Get the size of the mtd device while we're at it */
163 rc = get_dev_attr(dirent->d_name, "size", &size);
164 if (rc) {
165 free(namelist[i]);
166 continue;
167 }
168
169 rc = asprintf(&dev_path, "/dev/%s", dirent->d_name);
170 if (rc < 0) {
171 free(namelist[i]);
172 continue;
173 }
174 rc = 0;
175 *mtd_path = dev_path;
176 done = true;
177 }
178 free(namelist[i]);
179 }
180 free(namelist);
181
182 if (!done) {
183 fprintf(stderr, "Couldn't find '%s' corresponding MTD\n", fdt_flash_path);
184 fprintf(stderr, "Is your kernel new enough to expose MTD?\n");
185 }
186
187 /* explicit negative value so as to not return a libflash code */
188 return done ? rc : -1;
189 }
190
arch_init_blocklevel(const char * file,bool keep_alive)191 static struct blocklevel_device *arch_init_blocklevel(const char *file, bool keep_alive)
192 {
193 int rc;
194 struct blocklevel_device *new_bl = NULL;
195 char *real_file = NULL;
196
197 if (!file) {
198 rc = get_dev_mtd(FDT_FLASH_PATH, &real_file);
199 if (rc)
200 return NULL;
201 }
202
203 rc = file_init_path(file ? file : real_file, NULL, keep_alive, &new_bl);
204 if (rc)
205 new_bl = NULL;
206 free(real_file);
207 return new_bl;
208 }
209
210 /* Skiboot will worry about this for us */
arch_flash_set_wrprotect(struct blocklevel_device * bl,int set)211 int __attribute__((const)) arch_flash_set_wrprotect(struct blocklevel_device *bl, int set)
212 {
213 (void)bl;
214 (void)set;
215
216 return 0;
217 }
218
arch_flash_access(struct blocklevel_device * bl,enum flash_access access)219 enum flash_access __attribute__((const)) arch_flash_access(struct blocklevel_device *bl,
220 enum flash_access access)
221 {
222 (void)bl;
223
224 if (access != PNOR_MTD)
225 return ACCESS_INVAL;
226
227 return PNOR_MTD;
228 }
229
arch_flash_init(struct blocklevel_device ** r_bl,const char * file,bool keep_alive)230 int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive)
231 {
232 struct blocklevel_device *new_bl;
233
234 /*
235 * In theory here we should check that something crazy wasn't
236 * passed to arch_flash_access() and refuse to init.
237 * However, arch_flash_access won't accept anything except
238 * PNOR_MTD, if they want something different then they should
239 * have checked with arch_flash_access()
240 */
241 new_bl = arch_init_blocklevel(file, keep_alive);
242 if (!new_bl)
243 return -1;
244
245 *r_bl = new_bl;
246 return 0;
247 }
248
arch_flash_close(struct blocklevel_device * bl,const char * file)249 void arch_flash_close(struct blocklevel_device *bl, const char *file)
250 {
251 (void)file;
252
253 file_exit_close(bl);
254 }
255