discover/grub2: Allow to separate the --id argument using a space char
[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 #define SECURE_BOOT_HEADERS_SIZE 4096
35 #define ROM_MAGIC_NUMBER 0x17082011
36
37 struct flash_info {
38         /* Device information */
39         struct blocklevel_device        *bl;
40         struct ffs_handle               *ffs;
41         uint64_t                        size;   /* raw size of partition */
42         const char                      *path;
43         bool                            ecc;
44         uint32_t                        erase_granule;
45
46         /* Partition information */
47         uint32_t                        attr_part_idx;
48         uint32_t                        attr_data_pos;
49         uint32_t                        attr_data_len; /* Includes ECC bytes */
50
51         /* 'Other Side' info if present */
52         struct flash_info               *other_side;
53
54 };
55
56 static int partition_info(struct flash_info *info, const char *partition)
57 {
58         int rc;
59
60         rc = ffs_lookup_part(info->ffs, partition, &info->attr_part_idx);
61         if (rc) {
62                 pb_log("Failed to find %s partition\n", partition);
63                 return rc;
64         }
65
66         return ffs_part_info(info->ffs, info->attr_part_idx, NULL,
67                            &info->attr_data_pos, &info->attr_data_len,
68                            NULL, &info->ecc);
69 }
70
71 static struct flash_info *flash_setup_buffer(void *ctx, const char *partition)
72 {
73         struct flash_info *info;
74         struct ffs_handle *tmp = NULL;
75         int rc = 0;
76
77         if (!partition)
78                 return NULL;
79
80         info = talloc_zero(ctx, struct flash_info);
81         if (!info)
82                 return NULL;
83
84         rc = arch_flash_init(&info->bl, NULL, true);
85         if (rc) {
86                 pb_log("Failed to init mtd device\n");
87                 goto out;
88         }
89
90         rc = blocklevel_get_info(info->bl, &info->path, &info->size,
91                                  &info->erase_granule);
92         if (rc) {
93                 pb_log("Failed to retrieve blocklevel info\n");
94                 goto out_flash;
95         }
96
97         rc = ffs_init(0, info->size, info->bl, &info->ffs, 1);
98         if (rc) {
99                 pb_log_fn("Failed to init ffs\n");
100                 goto out_flash;
101         }
102
103         rc = partition_info(info, partition);
104         if (rc) {
105                 pb_log("Failed to retrieve partition info\n");
106                 goto out_ffs;
107         }
108
109         /* Check if there is a second flash side. If there is not, or
110          * we fail to recognise it, ignore it and continue */
111         ffs_next_side(info->ffs, &tmp, info->ecc);
112         if (tmp && ffs_equal(info->ffs, tmp) == false) {
113                 pb_debug("Other side present on MTD device\n");
114                 info->other_side = talloc_zero(info, struct flash_info);
115                 info->other_side->ffs = tmp;
116                 info->other_side->bl = info->bl;
117                 info->other_side->size = info->size;
118                 info->other_side->path = info->path;
119                 info->other_side->ecc = info->ecc;
120                 info->other_side->erase_granule = info->erase_granule;
121                 rc = partition_info(info->other_side, partition);
122                 if (rc)
123                         pb_log("Failed to retrieve partition info "
124                                "for other side - ignoring\n");
125         }
126
127         pb_debug("%s Details\n", partition);
128         pb_debug("\tName\t\t%s\n", info->path);
129         pb_debug("\tFlash Size\t%lu\n", info->size);
130         pb_debug("\tGranule\t\t%u\n", info->erase_granule);
131         pb_debug("\tECC\t\t%s\n", info->ecc ? "Protected" : "Unprotected");
132         pb_debug("\tCurrent Side info:\n");
133         pb_debug("\tIndex\t\t%u\n", info->attr_part_idx);
134         pb_debug("\tOffset\t\t%u\n", info->attr_data_pos);
135         pb_debug("\tPart. Size\t%u\n", info->attr_data_len);
136         if (info->other_side) {
137                 pb_debug("\tOther Side info:\n");
138                 pb_debug("\tIndex\t\t%u\n", info->other_side->attr_part_idx);
139                 pb_debug("\tOffset\t\t%u\n", info->other_side->attr_data_pos);
140                 pb_debug("\tPart. Size\t%u\n", info->other_side->attr_data_len);
141         }
142
143         return info;
144 out_ffs:
145         ffs_close(info->ffs);
146 out_flash:
147         arch_flash_close(info->bl, NULL);
148 out:
149         talloc_free(info);
150         return NULL;
151 }
152
153 /* See stb_is_container() in Skiboot */
154 static bool is_signed(char *buffer, uint32_t len)
155 {
156         if (!buffer || len <= SECURE_BOOT_HEADERS_SIZE)
157                 return false;
158         if (be32_to_cpu(*(uint32_t *)buffer) != ROM_MAGIC_NUMBER)
159                 return false;
160         return true;
161 }
162
163 int flash_parse_version(void *ctx, char ***versions, bool current)
164 {
165         char *saveptr, *tok,  **tmp, *buffer;
166         const char *delim = "\n";
167         struct flash_info *info, *cur_info;
168         uint32_t len;
169         int rc, n = 0;
170
171         saveptr = tok = NULL;
172         tmp = NULL;
173
174         info = flash_setup_buffer(ctx, "VERSION");
175         if (!info)
176                 return 0;
177
178         if (!current && !info->other_side)
179                 return 0;
180
181         cur_info = !current ? info->other_side : info;
182
183         len = cur_info->attr_data_len -  ecc_size(cur_info->attr_data_len);
184         buffer = talloc_array(cur_info, char, len);
185         if (!buffer) {
186                 pb_log_fn("Failed to init buffer!\n");
187                 goto out;
188         }
189
190         rc = blocklevel_read(cur_info->bl, cur_info->attr_data_pos,
191                              buffer, len);
192         if (rc) {
193                 pb_log("Failed to read VERSION partition\n");
194                 goto out;
195         }
196
197         /* Check if this partition is signed */
198         if (is_signed(buffer, len))
199                 buffer += SECURE_BOOT_HEADERS_SIZE;
200
201         /* open-power-platform */
202         tok = strtok_r(buffer, delim, &saveptr);
203         if (tok) {
204                 tmp = talloc_realloc(ctx, tmp, char *, n + 1);
205                 if (!tmp) {
206                         pb_log_fn("Failed to allocate memory\n");
207                         goto out;
208                 }
209                 tmp[n++] = talloc_strdup(ctx, tok);
210         }
211
212         tok = strtok_r(NULL, delim, &saveptr);
213         while (tok) {
214                 /* Ignore leading tab from subsequent lines */
215                 tmp = talloc_realloc(ctx, tmp, char *, n + 1);
216                 if (!tmp) {
217                         pb_log_fn("Failed to reallocate memory\n");
218                         n = 0;
219                         goto out;
220                 }
221                 tmp[n++] = talloc_strdup(ctx, tok + 1);
222                 tok = strtok_r(NULL, delim, &saveptr);
223         }
224
225 out:
226         pb_debug("%d version strings read from %s side\n",
227                  n, current ? "current" : "other");
228         arch_flash_close(info->bl, NULL);
229         talloc_free(info);
230         *versions = tmp;
231         return n;
232 }