~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/skiboot/external/opal-prd/pnor.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

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