Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2012 Google Inc. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include <stdint.h> |
| 17 | #include <string.h> |
Aaron Durbin | 243aa44 | 2013-04-24 17:31:49 -0500 | [diff] [blame] | 18 | #include <bootstate.h> |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 19 | #include <console/console.h> |
| 20 | #include <cbfs.h> |
Aaron Durbin | 0424c95 | 2015-03-28 23:56:22 -0500 | [diff] [blame] | 21 | #include <fmap.h> |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 22 | #include <ip_checksum.h> |
| 23 | #include <device/device.h> |
| 24 | #include <cbmem.h> |
| 25 | #include "pei_data.h" |
| 26 | #include "haswell.h" |
| 27 | #include <spi-generic.h> |
| 28 | #include <spi_flash.h> |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 29 | |
Kyösti Mälkki | 9b29aad | 2015-01-06 07:08:46 +0100 | [diff] [blame] | 30 | /* convert a pointer to flash area into the offset inside the flash */ |
| 31 | static inline u32 to_flash_offset(struct spi_flash *flash, void *p) { |
| 32 | return ((u32)p + flash->size); |
| 33 | } |
| 34 | |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 35 | static struct mrc_data_container *next_mrc_block( |
| 36 | struct mrc_data_container *mrc_cache) |
| 37 | { |
| 38 | /* MRC data blocks are aligned within the region */ |
| 39 | u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->mrc_data_size; |
| 40 | if (mrc_size & (MRC_DATA_ALIGN - 1UL)) { |
| 41 | mrc_size &= ~(MRC_DATA_ALIGN - 1UL); |
| 42 | mrc_size += MRC_DATA_ALIGN; |
| 43 | } |
| 44 | |
| 45 | u8 *region_ptr = (u8*)mrc_cache; |
| 46 | region_ptr += mrc_size; |
| 47 | return (struct mrc_data_container *)region_ptr; |
| 48 | } |
| 49 | |
| 50 | static int is_mrc_cache(struct mrc_data_container *mrc_cache) |
| 51 | { |
| 52 | return (!!mrc_cache) && (mrc_cache->mrc_signature == MRC_DATA_SIGNATURE); |
| 53 | } |
| 54 | |
| 55 | /* Right now, the offsets for the MRC cache area are hard-coded in the |
| 56 | * northbridge Kconfig if CONFIG_CHROMEOS is not set. In order to make |
| 57 | * this more flexible, there are two of options: |
| 58 | * - Have each mainboard Kconfig supply a hard-coded offset |
| 59 | * - Use CBFS |
| 60 | */ |
| 61 | static u32 get_mrc_cache_region(struct mrc_data_container **mrc_region_ptr) |
| 62 | { |
Aaron Durbin | 0424c95 | 2015-03-28 23:56:22 -0500 | [diff] [blame] | 63 | size_t region_size = 0; |
Patrick Georgi | 7a625da | 2015-07-27 23:17:14 +0200 | [diff] [blame] | 64 | *mrc_region_ptr = NULL; |
Aaron Durbin | 0424c95 | 2015-03-28 23:56:22 -0500 | [diff] [blame] | 65 | |
| 66 | if (IS_ENABLED(CONFIG_CHROMEOS)) { |
| 67 | struct region_device rdev; |
| 68 | |
| 69 | if (fmap_locate_area_as_rdev("RW_MRC_CACHE", &rdev) == 0) { |
| 70 | region_size = region_device_sz(&rdev); |
| 71 | *mrc_region_ptr = rdev_mmap_full(&rdev); |
| 72 | } |
| 73 | } else { |
Aaron Durbin | 899d13d | 2015-05-15 23:39:23 -0500 | [diff] [blame] | 74 | *mrc_region_ptr = cbfs_boot_map_with_leak("mrc.cache", |
Aaron Durbin | 0424c95 | 2015-03-28 23:56:22 -0500 | [diff] [blame] | 75 | CBFS_TYPE_MRC_CACHE, |
| 76 | ®ion_size); |
| 77 | } |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 78 | return region_size; |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | * Find the largest index block in the MRC cache. Return NULL if non is |
| 83 | * found. |
| 84 | */ |
| 85 | static struct mrc_data_container *find_current_mrc_cache_local |
| 86 | (struct mrc_data_container *mrc_cache, u32 region_size) |
| 87 | { |
| 88 | u32 region_end; |
| 89 | u32 entry_id = 0; |
| 90 | struct mrc_data_container *mrc_next = mrc_cache; |
| 91 | |
| 92 | region_end = (u32) mrc_cache + region_size; |
| 93 | |
| 94 | /* Search for the last filled entry in the region */ |
| 95 | while (is_mrc_cache(mrc_next)) { |
| 96 | entry_id++; |
| 97 | mrc_cache = mrc_next; |
| 98 | mrc_next = next_mrc_block(mrc_next); |
| 99 | if ((u32)mrc_next >= region_end) { |
| 100 | /* Stay in the MRC data region */ |
| 101 | break; |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | if (entry_id == 0) { |
| 106 | printk(BIOS_ERR, "%s: No valid MRC cache found.\n", __func__); |
| 107 | return NULL; |
| 108 | } |
| 109 | |
| 110 | /* Verify checksum */ |
| 111 | if (mrc_cache->mrc_checksum != |
| 112 | compute_ip_checksum(mrc_cache->mrc_data, |
| 113 | mrc_cache->mrc_data_size)) { |
| 114 | printk(BIOS_ERR, "%s: MRC cache checksum mismatch\n", __func__); |
| 115 | return NULL; |
| 116 | } |
| 117 | |
| 118 | printk(BIOS_DEBUG, "%s: picked entry %u from cache block\n", __func__, |
| 119 | entry_id - 1); |
| 120 | |
| 121 | return mrc_cache; |
| 122 | } |
| 123 | |
| 124 | /* SPI code needs malloc/free. |
| 125 | * Also unknown if writing flash from XIP-flash code is a good idea |
| 126 | */ |
Aaron Durbin | 4d3de7e | 2015-09-02 17:34:04 -0500 | [diff] [blame] | 127 | |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 128 | /* find the first empty block in the MRC cache area. |
| 129 | * If there's none, return NULL. |
| 130 | * |
| 131 | * @mrc_cache_base - base address of the MRC cache area |
| 132 | * @mrc_cache - current entry (for which we need to find next) |
| 133 | * @region_size - total size of the MRC cache area |
| 134 | */ |
| 135 | static struct mrc_data_container *find_next_mrc_cache |
| 136 | (struct mrc_data_container *mrc_cache_base, |
| 137 | struct mrc_data_container *mrc_cache, |
| 138 | u32 region_size) |
| 139 | { |
| 140 | u32 region_end = (u32) mrc_cache_base + region_size; |
| 141 | |
| 142 | mrc_cache = next_mrc_block(mrc_cache); |
| 143 | if ((u32)mrc_cache >= region_end) { |
| 144 | /* Crossed the boundary */ |
| 145 | mrc_cache = NULL; |
| 146 | printk(BIOS_DEBUG, "%s: no available entries found\n", |
| 147 | __func__); |
| 148 | } else { |
| 149 | printk(BIOS_DEBUG, |
| 150 | "%s: picked next entry from cache block at %p\n", |
| 151 | __func__, mrc_cache); |
| 152 | } |
| 153 | |
| 154 | return mrc_cache; |
| 155 | } |
| 156 | |
Aaron Durbin | 243aa44 | 2013-04-24 17:31:49 -0500 | [diff] [blame] | 157 | static void update_mrc_cache(void *unused) |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 158 | { |
| 159 | printk(BIOS_DEBUG, "Updating MRC cache data.\n"); |
| 160 | struct mrc_data_container *current = cbmem_find(CBMEM_ID_MRCDATA); |
| 161 | struct mrc_data_container *cache, *cache_base; |
| 162 | u32 cache_size; |
| 163 | |
| 164 | if (!current) { |
| 165 | printk(BIOS_ERR, "No MRC cache in cbmem. Can't update flash.\n"); |
| 166 | return; |
| 167 | } |
| 168 | if (current->mrc_data_size == -1) { |
| 169 | printk(BIOS_ERR, "MRC cache data in cbmem invalid.\n"); |
| 170 | return; |
| 171 | } |
| 172 | |
| 173 | cache_size = get_mrc_cache_region(&cache_base); |
| 174 | if (cache_base == NULL) { |
| 175 | printk(BIOS_ERR, "%s: could not find MRC cache area\n", |
| 176 | __func__); |
| 177 | return; |
| 178 | } |
| 179 | |
| 180 | /* |
| 181 | * we need to: |
| 182 | */ |
| 183 | // 0. compare MRC data to last mrc-cache block (exit if same) |
| 184 | cache = find_current_mrc_cache_local(cache_base, cache_size); |
| 185 | |
| 186 | if (cache && (cache->mrc_data_size == current->mrc_data_size) && |
| 187 | (memcmp(cache, current, cache->mrc_data_size) == 0)) { |
| 188 | printk(BIOS_DEBUG, |
| 189 | "MRC data in flash is up to date. No update.\n"); |
| 190 | return; |
| 191 | } |
| 192 | |
| 193 | // 1. use spi_flash_probe() to find the flash, then |
| 194 | spi_init(); |
Gabe Black | 1e18735 | 2014-03-27 20:37:03 -0700 | [diff] [blame] | 195 | struct spi_flash *flash = spi_flash_probe(0, 0); |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 196 | if (!flash) { |
| 197 | printk(BIOS_DEBUG, "Could not find SPI device\n"); |
| 198 | return; |
| 199 | } |
| 200 | |
| 201 | // 2. look up the first unused block |
| 202 | if (cache) |
| 203 | cache = find_next_mrc_cache(cache_base, cache, cache_size); |
| 204 | |
| 205 | /* |
| 206 | * 3. if no such place exists, erase entire mrc-cache range & use |
| 207 | * block 0. First time around the erase is not needed, but this is a |
| 208 | * small overhead for simpler code. |
| 209 | */ |
| 210 | if (!cache) { |
| 211 | printk(BIOS_DEBUG, |
| 212 | "Need to erase the MRC cache region of %d bytes at %p\n", |
| 213 | cache_size, cache_base); |
| 214 | |
Vladimir Serbinenko | e51210d | 2014-01-04 20:58:55 +0100 | [diff] [blame] | 215 | flash->erase(flash, to_flash_offset(flash, cache_base), cache_size); |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 216 | |
| 217 | /* we will start at the beginning again */ |
| 218 | cache = cache_base; |
| 219 | } |
| 220 | // 4. write mrc data with flash->write() |
| 221 | printk(BIOS_DEBUG, "Finally: write MRC cache update to flash at %p\n", |
| 222 | cache); |
Vladimir Serbinenko | e51210d | 2014-01-04 20:58:55 +0100 | [diff] [blame] | 223 | flash->write(flash, to_flash_offset(flash, cache), |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 224 | current->mrc_data_size + sizeof(*current), current); |
| 225 | } |
Aaron Durbin | 243aa44 | 2013-04-24 17:31:49 -0500 | [diff] [blame] | 226 | |
Aaron Durbin | 9ef9d85 | 2015-03-16 17:30:09 -0500 | [diff] [blame] | 227 | BOOT_STATE_INIT_ENTRY(BS_WRITE_TABLES, BS_ON_ENTRY, update_mrc_cache, NULL); |
Aaron Durbin | 76c3700 | 2012-10-30 09:03:43 -0500 | [diff] [blame] | 228 | |
| 229 | struct mrc_data_container *find_current_mrc_cache(void) |
| 230 | { |
| 231 | struct mrc_data_container *cache_base; |
| 232 | u32 cache_size; |
| 233 | |
| 234 | cache_size = get_mrc_cache_region(&cache_base); |
| 235 | if (cache_base == NULL) { |
| 236 | printk(BIOS_ERR, "%s: could not find MRC cache area\n", |
| 237 | __func__); |
| 238 | return NULL; |
| 239 | } |
| 240 | |
| 241 | /* |
| 242 | * we need to: |
| 243 | */ |
| 244 | // 0. compare MRC data to last mrc-cache block (exit if same) |
| 245 | return find_current_mrc_cache_local(cache_base, cache_size); |
| 246 | } |