Angel Pons | 986d50e | 2020-04-02 23:48:53 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Daisuke Nojiri | 477dd18 | 2014-10-08 11:32:23 -0700 | [diff] [blame] | 2 | |
Furquan Shaikh | 2a12e2e | 2016-07-25 11:48:03 -0700 | [diff] [blame] | 3 | #include <commonlib/region.h> |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 4 | #include <console/console.h> |
Yu-Ping Wu | 29c8fa4 | 2019-11-18 11:25:47 +0800 | [diff] [blame] | 5 | #include <fmap.h> |
Arthur Heymans | 61ef0e4 | 2023-01-12 11:29:57 +0100 | [diff] [blame] | 6 | #include <fmap_config.h> |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 7 | #include <string.h> |
Randall Spangler | 144c228 | 2014-12-03 17:35:53 -0800 | [diff] [blame] | 8 | #include <vb2_api.h> |
Philipp Deppenwiese | fea2429 | 2017-10-17 17:02:29 +0200 | [diff] [blame] | 9 | #include <security/vboot/vboot_common.h> |
| 10 | #include <security/vboot/vbnv.h> |
| 11 | #include <security/vboot/vbnv_layout.h> |
Daisuke Nojiri | 477dd18 | 2014-10-08 11:32:23 -0700 | [diff] [blame] | 12 | |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 13 | #define BLOB_SIZE VB2_NVDATA_SIZE |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 14 | |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 15 | struct vbnv_flash_ctx { |
| 16 | /* VBNV flash is initialized */ |
| 17 | int initialized; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 18 | |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 19 | /* Offset of the current nvdata in flash */ |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 20 | int blob_offset; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 21 | |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 22 | /* Offset of the topmost nvdata blob in flash */ |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 23 | int top_offset; |
Vadim Bendebury | 9e381a1 | 2015-01-10 23:11:43 -0800 | [diff] [blame] | 24 | |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 25 | /* Region to store and retrieve the VBNV contents. */ |
| 26 | struct region_device vbnv_dev; |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 27 | |
| 28 | /* Cache of the current nvdata */ |
| 29 | uint8_t cache[BLOB_SIZE]; |
| 30 | }; |
Arthur Heymans | 344e86b | 2019-11-20 19:47:10 +0100 | [diff] [blame] | 31 | static struct vbnv_flash_ctx vbnv_flash; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 32 | |
| 33 | /* |
| 34 | * This code assumes that flash is erased to 1-bits, and write operations can |
| 35 | * only change 1-bits to 0-bits. So if the new contents only change 1-bits to |
| 36 | * 0-bits, we can reuse the current blob. |
| 37 | */ |
| 38 | static inline uint8_t erase_value(void) |
| 39 | { |
| 40 | return 0xff; |
| 41 | } |
| 42 | |
| 43 | static inline int can_overwrite(uint8_t current, uint8_t new) |
| 44 | { |
| 45 | return (current & new) == new; |
| 46 | } |
| 47 | |
Arthur Heymans | 61ef0e4 | 2023-01-12 11:29:57 +0100 | [diff] [blame] | 48 | _Static_assert(FMAP_SECTION_RW_NVRAM_SIZE >= BLOB_SIZE, |
| 49 | "RW_NVRAM FMAP section not present or too small"); |
| 50 | |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 51 | static int init_vbnv(void) |
| 52 | { |
Arthur Heymans | 344e86b | 2019-11-20 19:47:10 +0100 | [diff] [blame] | 53 | struct vbnv_flash_ctx *ctx = &vbnv_flash; |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 54 | struct region_device *rdev = &ctx->vbnv_dev; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 55 | uint8_t buf[BLOB_SIZE]; |
| 56 | uint8_t empty_blob[BLOB_SIZE]; |
Randall Spangler | aa1d314 | 2016-08-26 16:01:16 -0700 | [diff] [blame] | 57 | int used_below, empty_above; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 58 | int offset; |
| 59 | int i; |
| 60 | |
Yu-Ping Wu | 29c8fa4 | 2019-11-18 11:25:47 +0800 | [diff] [blame] | 61 | if (fmap_locate_area_as_rdev_rw("RW_NVRAM", rdev) || |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 62 | region_device_sz(rdev) < BLOB_SIZE) { |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 63 | printk(BIOS_ERR, "%s: failed to locate NVRAM\n", __func__); |
| 64 | return 1; |
| 65 | } |
| 66 | |
| 67 | /* Prepare an empty blob to compare against. */ |
| 68 | for (i = 0; i < BLOB_SIZE; i++) |
| 69 | empty_blob[i] = erase_value(); |
| 70 | |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 71 | ctx->top_offset = region_device_sz(rdev) - BLOB_SIZE; |
Vadim Bendebury | 9e381a1 | 2015-01-10 23:11:43 -0800 | [diff] [blame] | 72 | |
Randall Spangler | aa1d314 | 2016-08-26 16:01:16 -0700 | [diff] [blame] | 73 | /* Binary search for the border between used and empty */ |
| 74 | used_below = 0; |
| 75 | empty_above = region_device_sz(rdev) / BLOB_SIZE; |
| 76 | |
| 77 | while (used_below + 1 < empty_above) { |
| 78 | int guess = (used_below + empty_above) / 2; |
| 79 | if (rdev_readat(rdev, buf, guess * BLOB_SIZE, BLOB_SIZE) < 0) { |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 80 | printk(BIOS_ERR, "failed to read nvdata\n"); |
| 81 | return 1; |
| 82 | } |
| 83 | if (!memcmp(buf, empty_blob, BLOB_SIZE)) |
Randall Spangler | aa1d314 | 2016-08-26 16:01:16 -0700 | [diff] [blame] | 84 | empty_above = guess; |
| 85 | else |
| 86 | used_below = guess; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 87 | } |
| 88 | |
Randall Spangler | aa1d314 | 2016-08-26 16:01:16 -0700 | [diff] [blame] | 89 | /* |
| 90 | * Offset points to the last non-empty blob. Or if all blobs are empty |
| 91 | * (nvram is totally erased), point to the first blob. |
| 92 | */ |
| 93 | offset = used_below * BLOB_SIZE; |
| 94 | |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 95 | /* reread the nvdata and write it to the cache */ |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 96 | if (rdev_readat(rdev, ctx->cache, offset, BLOB_SIZE) < 0) { |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 97 | printk(BIOS_ERR, "failed to read nvdata\n"); |
| 98 | return 1; |
| 99 | } |
| 100 | |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 101 | ctx->blob_offset = offset; |
| 102 | ctx->initialized = 1; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 103 | |
| 104 | return 0; |
| 105 | } |
| 106 | |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 107 | static int erase_nvram(void) |
| 108 | { |
Arthur Heymans | 344e86b | 2019-11-20 19:47:10 +0100 | [diff] [blame] | 109 | struct vbnv_flash_ctx *ctx = &vbnv_flash; |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 110 | const struct region_device *rdev = &ctx->vbnv_dev; |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 111 | |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 112 | if (rdev_eraseat(rdev, 0, region_device_sz(rdev)) < 0) { |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 113 | printk(BIOS_ERR, "failed to erase nvram\n"); |
| 114 | return 1; |
| 115 | } |
| 116 | |
| 117 | printk(BIOS_INFO, "nvram is cleared\n"); |
| 118 | return 0; |
| 119 | } |
| 120 | |
Duncan Laurie | 88b28ad | 2016-01-25 17:13:27 -0800 | [diff] [blame] | 121 | void read_vbnv_flash(uint8_t *vbnv_copy) |
Daisuke Nojiri | 477dd18 | 2014-10-08 11:32:23 -0700 | [diff] [blame] | 122 | { |
Arthur Heymans | 344e86b | 2019-11-20 19:47:10 +0100 | [diff] [blame] | 123 | struct vbnv_flash_ctx *ctx = &vbnv_flash; |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 124 | |
| 125 | if (!ctx->initialized) |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 126 | if (init_vbnv()) |
| 127 | return; /* error */ |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 128 | |
| 129 | memcpy(vbnv_copy, ctx->cache, BLOB_SIZE); |
Daisuke Nojiri | 477dd18 | 2014-10-08 11:32:23 -0700 | [diff] [blame] | 130 | } |
| 131 | |
Duncan Laurie | 88b28ad | 2016-01-25 17:13:27 -0800 | [diff] [blame] | 132 | void save_vbnv_flash(const uint8_t *vbnv_copy) |
Daisuke Nojiri | 477dd18 | 2014-10-08 11:32:23 -0700 | [diff] [blame] | 133 | { |
Arthur Heymans | 344e86b | 2019-11-20 19:47:10 +0100 | [diff] [blame] | 134 | struct vbnv_flash_ctx *ctx = &vbnv_flash; |
Vadim Bendebury | 9e381a1 | 2015-01-10 23:11:43 -0800 | [diff] [blame] | 135 | int new_offset; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 136 | int i; |
Aaron Durbin | b18a666 | 2016-08-12 12:48:58 -0500 | [diff] [blame] | 137 | const struct region_device *rdev = &ctx->vbnv_dev; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 138 | |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 139 | if (!ctx->initialized) |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 140 | if (init_vbnv()) |
| 141 | return; /* error */ |
| 142 | |
| 143 | /* Bail out if there have been no changes. */ |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 144 | if (!memcmp(vbnv_copy, ctx->cache, BLOB_SIZE)) |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 145 | return; |
| 146 | |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 147 | new_offset = ctx->blob_offset; |
Vadim Bendebury | 9e381a1 | 2015-01-10 23:11:43 -0800 | [diff] [blame] | 148 | |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 149 | /* See if we can overwrite the current blob with the new one */ |
| 150 | for (i = 0; i < BLOB_SIZE; i++) { |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 151 | if (!can_overwrite(ctx->cache[i], vbnv_copy[i])) { |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 152 | /* unable to overwrite. need to use the next blob */ |
| 153 | new_offset += BLOB_SIZE; |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 154 | if (new_offset > ctx->top_offset) { |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 155 | if (erase_nvram()) |
| 156 | return; /* error */ |
Aaron Durbin | 4e50cdd | 2015-05-15 23:25:46 -0500 | [diff] [blame] | 157 | new_offset = 0; |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 158 | } |
| 159 | break; |
| 160 | } |
| 161 | } |
| 162 | |
Aaron Durbin | 1b5581c | 2016-08-23 13:22:17 -0500 | [diff] [blame] | 163 | if (rdev_writeat(rdev, vbnv_copy, new_offset, BLOB_SIZE) == BLOB_SIZE) { |
Vadim Bendebury | 9e381a1 | 2015-01-10 23:11:43 -0800 | [diff] [blame] | 164 | /* write was successful. safely move pointer forward */ |
Duncan Laurie | 0165038 | 2016-01-26 16:22:53 -0800 | [diff] [blame] | 165 | ctx->blob_offset = new_offset; |
| 166 | memcpy(ctx->cache, vbnv_copy, BLOB_SIZE); |
Vadim Bendebury | 9e381a1 | 2015-01-10 23:11:43 -0800 | [diff] [blame] | 167 | } else { |
| 168 | printk(BIOS_ERR, "failed to save nvdata\n"); |
Daisuke Nojiri | 2b59a83 | 2014-10-29 11:18:11 -0700 | [diff] [blame] | 169 | } |
Daisuke Nojiri | 477dd18 | 2014-10-08 11:32:23 -0700 | [diff] [blame] | 170 | } |