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 #include <libflash/libffs.h>
18 #include <common/arch_flash.h>
19 
20 #include <errno.h>
21 
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <sys/ioctl.h>
30 #include <mtd/mtd-user.h>
31 
32 #include "pnor.h"
33 #include "opal-prd.h"
34 
35 #define FDT_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash"
36 
pnor_available(struct pnor * pnor)37 bool pnor_available(struct pnor *pnor)
38 {
39 	/* --pnor is specified */
40 	if (pnor->path) {
41 		if (access(pnor->path, R_OK | W_OK) == 0)
42 			return true;
43 
44 		pr_log(LOG_ERR, "PNOR: Does not have permission to read pnor: %m");
45 		return false;
46 	}
47 
48 	if (access(FDT_FLASH_PATH, R_OK) == 0)
49 		return true;
50 
51 	return false;
52 }
53 
pnor_init(struct pnor * pnor)54 int pnor_init(struct pnor *pnor)
55 {
56 	int rc;
57 
58 	if (!pnor)
59 		return -1;
60 
61 	rc = arch_flash_init(&(pnor->bl), pnor->path, false);
62 	if (rc) {
63 		pr_log(LOG_ERR, "PNOR: Flash init failed");
64 		return -1;
65 	}
66 
67 	rc = blocklevel_get_info(pnor->bl, NULL, &(pnor->size), &(pnor->erasesize));
68 	if (rc) {
69 		pr_log(LOG_ERR, "PNOR: blocklevel_get_info() failed. Can't use PNOR");
70 		goto out;
71 	}
72 
73 	rc = ffs_init(0, pnor->size, pnor->bl, &pnor->ffsh, 0);
74 	if (rc) {
75 		pr_log(LOG_ERR, "PNOR: Failed to open pnor partition table");
76 		goto out;
77 	}
78 
79 	return 0;
80 out:
81 	arch_flash_close(pnor->bl, pnor->path);
82 	pnor->bl = NULL;
83 	return -1;
84 }
85 
pnor_close(struct pnor * pnor)86 void pnor_close(struct pnor *pnor)
87 {
88 	if (!pnor)
89 		return;
90 
91 	if (pnor->ffsh)
92 		ffs_close(pnor->ffsh);
93 
94 	if (pnor->bl)
95 		arch_flash_close(pnor->bl, pnor->path);
96 
97 	if (pnor->path)
98 		free(pnor->path);
99 }
100 
dump_parts(struct ffs_handle * ffs)101 void dump_parts(struct ffs_handle *ffs) {
102 	int i, rc;
103 	uint32_t start, size, act_size;
104 	char *name;
105 
106 	pr_debug("PNOR: %10s %8s %8s %8s",
107 			"name", "start", "size", "act_size");
108 	for (i = 0; ; i++) {
109 		rc = ffs_part_info(ffs, i, &name, &start,
110 				&size, &act_size, NULL);
111 		if (rc)
112 			break;
113 		pr_debug("PNOR: %10s %08x %08x %08x",
114 				name, start, size, act_size);
115 		free(name);
116 	}
117 }
118 
mtd_write(struct pnor * pnor,void * data,uint64_t offset,size_t len)119 static int mtd_write(struct pnor *pnor, void *data, uint64_t offset,
120 		     size_t len)
121 {
122 	int rc;
123 
124 	if (len > pnor->size || offset > pnor->size ||
125 	    len + offset > pnor->size)
126 		return -ERANGE;
127 
128 	rc = blocklevel_smart_write(pnor->bl, offset, data, len);
129 	if (rc)
130 		return -errno;
131 
132 	return len;
133 }
134 
mtd_read(struct pnor * pnor,void * data,uint64_t offset,size_t len)135 static int mtd_read(struct pnor *pnor, void *data, uint64_t offset,
136 		    size_t len)
137 {
138 	int rc;
139 
140 	if (len > pnor->size || offset > pnor->size ||
141 	    len + offset > pnor->size)
142 		return -ERANGE;
143 
144 	rc = blocklevel_read(pnor->bl, offset, data, len);
145 	if (rc)
146 		return -errno;
147 
148 	return len;
149 }
150 
151 /* Similar to read(2), this performs partial operations where the number of
152  * bytes read/written may be less than size.
153  *
154  * Returns number of bytes written, or a negative value on failure. */
pnor_operation(struct pnor * pnor,const char * name,uint64_t offset,void * data,size_t requested_size,enum pnor_op op)155 int pnor_operation(struct pnor *pnor, const char *name, uint64_t offset,
156 		   void *data, size_t requested_size, enum pnor_op op)
157 {
158 	int rc;
159 	uint32_t pstart, psize, idx;
160 	int size;
161 
162 	if (!pnor->ffsh) {
163 		pr_log(LOG_ERR, "PNOR: ffs not initialised");
164 		return -EBUSY;
165 	}
166 
167 	rc = ffs_lookup_part(pnor->ffsh, name, &idx);
168 	if (rc) {
169 		pr_log(LOG_WARNING, "PNOR: no partiton named '%s'", name);
170 		return -ENOENT;
171 	}
172 
173 	ffs_part_info(pnor->ffsh, idx, NULL, &pstart, &psize, NULL, NULL);
174 	if (rc) {
175 		pr_log(LOG_ERR, "PNOR: unable to fetch partition info for %s",
176 				name);
177 		return -ENOENT;
178 	}
179 
180 	if (offset > psize) {
181 		pr_log(LOG_WARNING, "PNOR: partition %s(size 0x%x) "
182 				"offset (0x%lx) out of bounds",
183 				name, psize, offset);
184 		return -ERANGE;
185 	}
186 
187 	/* Large requests are trimmed */
188 	if (requested_size > psize)
189 		size = psize;
190 	else
191 		size = requested_size;
192 
193 	if (size + offset > psize)
194 		size = psize - offset;
195 
196 	if (size < 0) {
197 		pr_log(LOG_WARNING, "PNOR: partition %s(size 0x%x) "
198 				"read size (0x%zx) and offset (0x%lx) "
199 				"out of bounds",
200 				name, psize, requested_size, offset);
201 		return -ERANGE;
202 	}
203 
204 	switch (op) {
205 	case PNOR_OP_READ:
206 		rc = mtd_read(pnor, data, pstart + offset, size);
207 		break;
208 	case PNOR_OP_WRITE:
209 		rc = mtd_write(pnor, data, pstart + offset, size);
210 		break;
211 	default:
212 		rc  = -EIO;
213 		pr_log(LOG_ERR, "PNOR: Invalid operation");
214 		goto out;
215 	}
216 
217 	if (rc < 0)
218 		pr_log(LOG_ERR, "PNOR: MTD operation failed");
219 	else if (rc != size)
220 		pr_log(LOG_WARNING, "PNOR: mtd operation "
221 				"returned %d, expected %d",
222 				rc, size);
223 
224 out:
225 	return rc;
226 }
227