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