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