discover: Track both configured and current autoboot settings
[petitboot] / lib / flash / flash.c
1 /*
2  *  Copyright (C) 2015 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  */
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <getopt.h>
19 #include <errno.h>
20 #include <asm/byteorder.h>
21
22 #include <log/log.h>
23 #include <talloc/talloc.h>
24 #include <flash/flash.h>
25 #include <ccan/endian/endian.h>
26
27 #include <libflash/arch_flash.h>
28 #include <libflash/blocklevel.h>
29 #include <libflash/libflash.h>
30 #include <libflash/libffs.h>
31 #include <libflash/file.h>
32 #include <libflash/ecc.h>
33
34
35 struct flash_info {
36         /* Device information */
37         struct blocklevel_device        *bl;
38         struct ffs_handle               *ffs;
39         uint32_t                        size;   /* raw size of partition */
40         const char                      *path;
41         bool                            ecc;
42         uint32_t                        erase_granule;
43
44         /* Partition information */
45         uint32_t                        attr_part_idx;
46         uint32_t                        attr_data_pos;
47         uint32_t                        attr_data_len; /* Includes ECC bytes */
48
49         /* 'Other Side' info if present */
50         struct flash_info               *other_side;
51
52 };
53
54 static int partition_info(struct flash_info *info, const char *partition)
55 {
56         int rc;
57
58         rc = ffs_lookup_part(info->ffs, partition, &info->attr_part_idx);
59         if (rc) {
60                 pb_log("Failed to find %s partition\n", partition);
61                 return rc;
62         }
63
64         return ffs_part_info(info->ffs, info->attr_part_idx, NULL,
65                            &info->attr_data_pos, &info->attr_data_len,
66                            NULL, &info->ecc);
67 }
68
69 static struct flash_info *flash_setup_buffer(void *ctx, const char *partition)
70 {
71         struct flash_info *info;
72         struct ffs_handle *tmp = NULL;
73         int rc = 0;
74
75         if (!partition)
76                 return NULL;
77
78         info = talloc_zero(ctx, struct flash_info);
79         if (!info)
80                 return NULL;
81
82         rc = arch_flash_init(&info->bl, NULL, true);
83         if (rc) {
84                 pb_log("Failed to init mtd device\n");
85                 goto out;
86         }
87
88         rc = blocklevel_get_info(info->bl, &info->path, &info->size,
89                                  &info->erase_granule);
90         if (rc) {
91                 pb_log("Failed to retrieve blocklevel info\n");
92                 goto out_flash;
93         }
94
95         rc = ffs_init(0, info->size, info->bl, &info->ffs, 1);
96         if (rc) {
97                 pb_log("%s: Failed to init ffs\n", __func__);
98                 goto out_flash;
99         }
100
101         rc = partition_info(info, partition);
102         if (rc) {
103                 pb_log("Failed to retrieve partition info\n");
104                 goto out_ffs;
105         }
106
107         /* Check if there is a second flash side. If there is not, or
108          * we fail to recognise it, ignore it and continue */
109         ffs_next_side(info->ffs, &tmp, info->ecc);
110         if (tmp && ffs_equal(info->ffs, tmp) == false) {
111                 pb_debug("Other side present on MTD device\n");
112                 info->other_side = talloc_zero(info, struct flash_info);
113                 info->other_side->ffs = tmp;
114                 info->other_side->bl = info->bl;
115                 info->other_side->size = info->size;
116                 info->other_side->path = info->path;
117                 info->other_side->ecc = info->ecc;
118                 info->other_side->erase_granule = info->erase_granule;
119                 rc = partition_info(info->other_side, partition);
120                 if (rc)
121                         pb_log("Failed to retrieve partition info "
122                                "for other side - ignoring\n");
123         }
124
125         pb_debug("%s Details\n", partition);
126         pb_debug("\tName\t\t%s\n", info->path);
127         pb_debug("\tFlash Size\t%u\n", info->size);
128         pb_debug("\tGranule\t\t%u\n", info->erase_granule);
129         pb_debug("\tECC\t\t%s\n", info->ecc ? "Protected" : "Unprotected");
130         pb_debug("\tCurrent Side info:\n");
131         pb_debug("\tIndex\t\t%u\n", info->attr_part_idx);
132         pb_debug("\tOffset\t\t%u\n", info->attr_data_pos);
133         pb_debug("\tPart. Size\t%u\n", info->attr_data_len);
134         if (info->other_side) {
135                 pb_debug("\tOther Side info:\n");
136                 pb_debug("\tIndex\t\t%u\n", info->other_side->attr_part_idx);
137                 pb_debug("\tOffset\t\t%u\n", info->other_side->attr_data_pos);
138                 pb_debug("\tPart. Size\t%u\n", info->other_side->attr_data_len);
139         }
140
141         return info;
142 out_ffs:
143         ffs_close(info->ffs);
144 out_flash:
145         arch_flash_close(info->bl, NULL);
146 out:
147         talloc_free(info);
148         return NULL;
149 }
150
151 int flash_parse_version(void *ctx, char ***versions, bool current)
152 {
153         char *saveptr, *tok,  **tmp, *buffer;
154         const char *delim = "\n";
155         struct flash_info *info, *cur_info;
156         uint32_t len;
157         int rc, n = 0;
158
159         saveptr = tok = NULL;
160         tmp = NULL;
161
162         info = flash_setup_buffer(ctx, "VERSION");
163         if (!info)
164                 return 0;
165
166         if (!current && !info->other_side)
167                 return 0;
168
169         cur_info = !current ? info->other_side : info;
170
171         len = cur_info->attr_data_len -  ecc_size(cur_info->attr_data_len);
172         buffer = talloc_array(cur_info, char, len);
173         if (!buffer) {
174                 pb_log("%s: Failed to init buffer!\n", __func__);
175                 goto out;
176         }
177
178         rc = blocklevel_read(cur_info->bl, cur_info->attr_data_pos,
179                              buffer, len);
180         if (rc) {
181                 pb_log("Failed to read VERSION partition\n");
182                 goto out;
183         }
184
185         /* open-power-platform */
186         tok = strtok_r(buffer, delim, &saveptr);
187         if (tok) {
188                 tmp = talloc_realloc(ctx, tmp, char *, n + 1);
189                 if (!tmp) {
190                         pb_log("%s: Failed to allocate memory\n", __func__);
191                         goto out;
192                 }
193                 tmp[n++] = talloc_strdup(ctx, tok);
194         }
195
196         tok = strtok_r(NULL, delim, &saveptr);
197         while (tok) {
198                 /* Ignore leading tab from subsequent lines */
199                 tmp = talloc_realloc(ctx, tmp, char *, n + 1);
200                 if (!tmp) {
201                         pb_log("%s: Failed to reallocate memory\n", __func__);
202                         n = 0;
203                         goto out;
204                 }
205                 tmp[n++] = talloc_strdup(ctx, tok + 1);
206                 tok = strtok_r(NULL, delim, &saveptr);
207         }
208
209 out:
210         pb_debug("%d version strings read from %s side\n",
211                  n, current ? "current" : "other");
212         arch_flash_close(info->bl, NULL);
213         talloc_free(info);
214         *versions = tmp;
215         return n;
216 }