Angel Pons | 6bc1374 | 2020-04-05 15:46:38 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 2 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 3 | #include <device/mmio.h> |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 4 | #include <bootstate.h> |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 5 | #include <commonlib/region.h> |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 6 | #include <console/console.h> |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 7 | #include <fmap.h> |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 8 | #include <intelblocks/cse.h> |
Furquan Shaikh | d2c2f83 | 2018-11-07 10:24:31 -0800 | [diff] [blame] | 9 | #include <intelblocks/p2sb.h> |
| 10 | #include <intelblocks/pcr.h> |
| 11 | #include <soc/heci.h> |
| 12 | #include <soc/iomap.h> |
| 13 | #include <soc/pcr_ids.h> |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 14 | #include <soc/pci_devs.h> |
Patrick Rudolph | e56189c | 2018-04-18 10:11:59 +0200 | [diff] [blame] | 15 | #include <device/pci_ops.h> |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 16 | #include <stdint.h> |
| 17 | |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 18 | #define MKHI_GROUP_ID_MCA 0x0a |
| 19 | #define READ_FILE 0x02 |
| 20 | #define READ_FILE_FLAG_DEFAULT (1 << 0) |
| 21 | #define READ_FILE_FLAG_HASH (1 << 1) |
| 22 | #define READ_FILE_FLAG_EMULATED (1 << 2) |
| 23 | #define READ_FILE_FLAG_HW (1 << 3) |
| 24 | |
| 25 | #define MCA_MAX_FILE_PATH_SIZE 64 |
| 26 | |
| 27 | #define FUSE_LOCK_FILE "/fpf/intel/SocCfgLock" |
| 28 | |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 29 | /* Status values are made in such a way erase is not needed */ |
| 30 | static enum fuse_flash_state { |
| 31 | FUSE_FLASH_FUSED = 0xfc, |
| 32 | FUSE_FLASH_UNFUSED = 0xfe, |
| 33 | FUSE_FLASH_UNKNOWN = 0xff, |
| 34 | } g_fuse_state; |
| 35 | |
| 36 | #define FPF_STATUS_FMAP "FPF_STATUS" |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 37 | |
| 38 | /* |
| 39 | * Read file from CSE internal filesystem. |
| 40 | * size is maximum length of provided buffer buff, which is updated with actual |
| 41 | * size of the file read. flags indicate whether real file or fuse is used. |
| 42 | * Returns 1 on success and 0 otherwise. |
| 43 | */ |
| 44 | static int read_cse_file(const char *path, void *buff, size_t *size, |
| 45 | size_t offset, uint32_t flags) |
| 46 | { |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 47 | size_t reply_size; |
| 48 | |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 49 | struct mca_command { |
Sridhar Siricilla | f35eee9 | 2019-09-23 19:38:21 +0530 | [diff] [blame] | 50 | struct mkhi_hdr hdr; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 51 | char file_name[MCA_MAX_FILE_PATH_SIZE]; |
| 52 | uint32_t offset; |
| 53 | uint32_t data_size; |
| 54 | uint8_t flags; |
Stefan Reinauer | 6a00113 | 2017-07-13 02:20:27 +0200 | [diff] [blame] | 55 | } __packed msg; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 56 | |
| 57 | struct mca_response { |
Sridhar Siricilla | f35eee9 | 2019-09-23 19:38:21 +0530 | [diff] [blame] | 58 | struct mkhi_hdr hdr; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 59 | uint32_t data_size; |
| 60 | uint8_t buffer[128]; |
Stefan Reinauer | 6a00113 | 2017-07-13 02:20:27 +0200 | [diff] [blame] | 61 | } __packed rmsg; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 62 | |
| 63 | if (sizeof(rmsg.buffer) < *size) { |
| 64 | printk(BIOS_ERR, "internal buffer is too small\n"); |
| 65 | return 0; |
| 66 | } |
| 67 | |
Hannah Williams | 58810c7 | 2017-08-02 18:13:33 -0700 | [diff] [blame] | 68 | if (strnlen(path, sizeof(msg.file_name)) >= sizeof(msg.file_name)) { |
| 69 | printk(BIOS_ERR, "path too big for msg.file_name buffer\n"); |
| 70 | return 0; |
| 71 | } |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 72 | strncpy(msg.file_name, path, sizeof(msg.file_name)); |
Sridhar Siricilla | f35eee9 | 2019-09-23 19:38:21 +0530 | [diff] [blame] | 73 | msg.hdr.group_id = MKHI_GROUP_ID_MCA; |
| 74 | msg.hdr.command = READ_FILE; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 75 | msg.flags = flags; |
| 76 | msg.data_size = *size; |
| 77 | msg.offset = offset; |
| 78 | |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 79 | reply_size = sizeof(rmsg); |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 80 | |
Sridhar Siricilla | 6836da2 | 2022-02-23 23:36:45 +0530 | [diff] [blame] | 81 | if (heci_send_receive(&msg, sizeof(msg), &rmsg, &reply_size, HECI_MKHI_ADDR)) { |
Rizwan Qureshi | 957857d | 2021-08-30 16:43:57 +0530 | [diff] [blame] | 82 | printk(BIOS_ERR, "HECI: Failed to read file\n"); |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 83 | return 0; |
| 84 | } |
| 85 | |
| 86 | if (rmsg.data_size > *size) { |
| 87 | printk(BIOS_ERR, "reply is too large\n"); |
| 88 | return 0; |
| 89 | } |
| 90 | |
| 91 | memcpy(buff, rmsg.buffer, rmsg.data_size); |
| 92 | *size = rmsg.data_size; |
| 93 | |
| 94 | return 1; |
| 95 | } |
| 96 | |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 97 | static enum fuse_flash_state load_cached_fpf(struct region_device *rdev) |
| 98 | { |
| 99 | enum fuse_flash_state state; |
| 100 | uint8_t buff; |
| 101 | |
| 102 | state = FUSE_FLASH_UNKNOWN; |
| 103 | |
| 104 | if (rdev_readat(rdev, &buff, 0, sizeof(buff)) >= 0) { |
| 105 | state = read8(&buff); |
| 106 | return state; |
| 107 | } |
| 108 | |
| 109 | printk(BIOS_WARNING, "failed to load cached FPF value\n"); |
| 110 | |
| 111 | return state; |
| 112 | } |
| 113 | |
| 114 | static |
| 115 | int save_fpf_state(enum fuse_flash_state state, struct region_device *rdev) |
| 116 | { |
| 117 | uint8_t buff; |
| 118 | |
| 119 | write8(&buff, (uint8_t) state); |
| 120 | return rdev_writeat(rdev, &buff, 0, sizeof(buff)); |
| 121 | } |
| 122 | |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 123 | static void fpf_blown(void *unused) |
| 124 | { |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 125 | uint8_t fuse; |
| 126 | struct region_device rdev; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 127 | size_t sz = sizeof(fuse); |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 128 | bool rdev_valid = false; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 129 | |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 130 | if (fmap_locate_area_as_rdev_rw(FPF_STATUS_FMAP, &rdev) == 0) { |
| 131 | rdev_valid = true; |
| 132 | g_fuse_state = load_cached_fpf(&rdev); |
| 133 | if (g_fuse_state != FUSE_FLASH_UNKNOWN) |
| 134 | return; |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 135 | } |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 136 | |
| 137 | if (!read_cse_file(FUSE_LOCK_FILE, &fuse, &sz, 0, READ_FILE_FLAG_HW)) |
| 138 | return; |
| 139 | |
| 140 | g_fuse_state = fuse == 1 ? FUSE_FLASH_FUSED : FUSE_FLASH_UNFUSED; |
| 141 | |
| 142 | if (rdev_valid && (save_fpf_state(g_fuse_state, &rdev) < 0)) |
| 143 | printk(BIOS_CRIT, "failed to save FPF state\n"); |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 144 | } |
| 145 | |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 146 | static uint32_t dump_status(int index, int reg_addr) |
| 147 | { |
Sridhar Siricilla | 2cc6691 | 2019-08-31 11:20:34 +0530 | [diff] [blame] | 148 | uint32_t reg; |
| 149 | |
| 150 | reg = me_read_config32(reg_addr); |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 151 | |
| 152 | printk(BIOS_DEBUG, "CSE FWSTS%d: 0x%08x\n", index, reg); |
| 153 | |
| 154 | return reg; |
| 155 | } |
| 156 | |
Furquan Shaikh | d2c2f83 | 2018-11-07 10:24:31 -0800 | [diff] [blame] | 157 | static void dump_cse_state(void) |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 158 | { |
| 159 | uint32_t fwsts1; |
| 160 | |
Sridhar Siricilla | 2cc6691 | 2019-08-31 11:20:34 +0530 | [diff] [blame] | 161 | if (!is_cse_enabled()) |
| 162 | return; |
| 163 | |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 164 | fwsts1 = dump_status(1, PCI_ME_HFSTS1); |
| 165 | dump_status(2, PCI_ME_HFSTS2); |
| 166 | dump_status(3, PCI_ME_HFSTS3); |
| 167 | dump_status(4, PCI_ME_HFSTS4); |
| 168 | dump_status(5, PCI_ME_HFSTS5); |
| 169 | dump_status(6, PCI_ME_HFSTS6); |
| 170 | |
| 171 | /* Minimal decoding is done here in order to call out most important |
| 172 | pieces. Manufacturing mode needs to be locked down prior to shipping |
| 173 | the product so it's called out explicitly. */ |
| 174 | printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n", |
| 175 | (fwsts1 & (1 << 0x4)) ? "YES" : "NO"); |
Aaron Durbin | 7d14af8 | 2017-02-07 11:33:56 -0600 | [diff] [blame] | 176 | |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 177 | printk(BIOS_DEBUG, "ME: FPF status : "); |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 178 | switch (g_fuse_state) { |
| 179 | case FUSE_FLASH_UNFUSED: |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 180 | printk(BIOS_DEBUG, "unfused"); |
| 181 | break; |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 182 | case FUSE_FLASH_FUSED: |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 183 | printk(BIOS_DEBUG, "fused"); |
| 184 | break; |
| 185 | default: |
Andrey Petrov | c5f3685 | 2017-03-12 00:51:34 -0800 | [diff] [blame] | 186 | case FUSE_FLASH_UNKNOWN: |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 187 | printk(BIOS_DEBUG, "unknown"); |
| 188 | } |
| 189 | printk(BIOS_DEBUG, "\n"); |
| 190 | } |
Furquan Shaikh | d2c2f83 | 2018-11-07 10:24:31 -0800 | [diff] [blame] | 191 | |
| 192 | #define PCR_PSFX_T0_SHDW_PCIEN 0x1C |
| 193 | #define PCR_PSFX_T0_SHDW_PCIEN_FUNDIS (1 << 8) |
| 194 | |
Subrata Banik | ea47c6b | 2022-01-28 13:12:58 +0530 | [diff] [blame] | 195 | void soc_disable_heci1_using_pcr(void) |
Furquan Shaikh | d2c2f83 | 2018-11-07 10:24:31 -0800 | [diff] [blame] | 196 | { |
| 197 | pcr_or32(PID_PSF3, PSF3_BASE_ADDRESS + PCR_PSFX_T0_SHDW_PCIEN, |
| 198 | PCR_PSFX_T0_SHDW_PCIEN_FUNDIS); |
| 199 | } |
| 200 | |
| 201 | void heci_cse_lockdown(void) |
| 202 | { |
| 203 | dump_cse_state(); |
| 204 | |
| 205 | /* |
| 206 | * It is safe to disable HECI1 now since we won't be talking to the ME |
| 207 | * anymore. |
| 208 | */ |
Subrata Banik | 206b0bc | 2022-01-06 09:34:43 +0000 | [diff] [blame] | 209 | if (CONFIG(DISABLE_HECI1_AT_PRE_BOOT)) |
Subrata Banik | ea47c6b | 2022-01-28 13:12:58 +0530 | [diff] [blame] | 210 | heci1_disable(); |
Furquan Shaikh | d2c2f83 | 2018-11-07 10:24:31 -0800 | [diff] [blame] | 211 | } |
| 212 | |
Andrey Petrov | b1aded2 | 2017-03-03 08:12:36 -0800 | [diff] [blame] | 213 | BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, fpf_blown, NULL); |
Sridhar Siricilla | 24a974a | 2020-02-19 14:41:36 +0530 | [diff] [blame] | 214 | BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, print_me_fw_version, NULL); |