coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
eeprom.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <assert.h>
4 #include <console/console.h>
5 #include <crc_byte.h>
6 #include <delay.h>
7 #include <device/pci_ops.h>
8 #include <device/smbus_host.h>
10 #include <types.h>
11 
12 #include "eeprom.h"
13 
14 #define I2C_ADDR_EEPROM 0x57
15 
16 /*
17  * Check Signature in EEPROM (M24C32-FMN6TP)
18  * If signature is there we assume that that the content is valid
19  */
20 int check_signature(const size_t offset, const uint64_t signature)
21 {
22  u8 blob[8] = {0};
23 
24  if (!eeprom_read_buffer(blob, offset, ARRAY_SIZE(blob))) {
25  /* Check signature */
26  if (*(uint64_t *)blob == signature) {
27  printk(BIOS_DEBUG, "CFG EEPROM: Signature valid.\n");
28  return 1;
29  }
30  printk(BIOS_DEBUG, "CFG EEPROM: Signature invalid - skipping option write.\n");
31  return 0;
32  }
33  return 0;
34 }
35 
36 /*
37  * Read board settings from the EEPROM and verify their checksum.
38  * If checksum is valid, we assume the settings are sane as well.
39  */
41 {
42  const size_t board_settings_offset = offsetof(struct eeprom_layout, BoardSettings);
43 
44  if (eeprom_read_buffer(board_cfg, board_settings_offset, sizeof(*board_cfg))) {
45  printk(BIOS_ERR, "CFG EEPROM: Failed to read board settings\n");
46  return false;
47  }
48 
49  const uint32_t crc =
50  CRC(&board_cfg->raw_settings, sizeof(board_cfg->raw_settings), crc32_byte);
51 
52  if (crc != board_cfg->signature) {
53  printk(BIOS_ERR, "CFG EEPROM: Board settings have invalid checksum\n");
54  return false;
55  }
56  return true;
57 }
58 
60 {
61  static struct eeprom_board_settings board_cfg = {0};
62 
63  /* Tri-state: -1: settings are invalid, 0: uninitialized, 1: settings are valid */
64  static int checked_valid = 0;
65 
66  if (checked_valid == 0) {
67  const bool success = get_board_settings_from_eeprom(&board_cfg);
68  checked_valid = success ? 1 : -1;
69  }
70  return checked_valid > 0 ? &board_cfg : NULL;
71 }
72 
74 {
75  const size_t bmc_settings_offset = offsetof(struct eeprom_layout, BMCSettings);
76  static struct eeprom_bmc_settings bmc_cfg = {0};
77 
78  /* 0: uninitialized, 1: settings are valid */
79  static int valid = 0;
80 
81  if (valid == 0) {
82  if (eeprom_read_buffer(&bmc_cfg, bmc_settings_offset, sizeof(bmc_cfg))) {
83  printk(BIOS_ERR, "CFG EEPROM: Failed to read BMC settings\n");
84  return NULL;
85  }
86  valid = 1;
87  }
88  return &bmc_cfg;
89 }
90 
92 {
93  uint8_t hsi = 0;
95  if (s)
96  hsi = s->hsi;
97  printk(BIOS_DEBUG, "CFG EEPROM: HSI 0x%x\n", hsi);
98 
99  return hsi;
100 }
101 
102 /* Read data from an EEPROM on SMBus and write it to a buffer */
103 bool eeprom_read_buffer(void *blob, size_t read_offset, size_t size)
104 {
105  int ret = 0;
106 
107  u32 smb_ctrl_reg = pci_read_config32(PCH_DEV_SMBUS, HOSTC);
108  pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg | I2C_EN);
109 
110  printk(BIOS_SPEW, "%s\tOffset: %04zx\tSize: %02zx\n", __func__,
111  read_offset, size);
112 
113  /* We can always read two bytes at a time */
114  for (size_t i = 0; i < size; i = i + 2) {
115  u8 tmp[2] = {0};
116 
118  swab16(read_offset + i), (uint16_t *)&tmp[0]);
119  if (ret < 0)
120  break;
121 
122  /* Write to UPD */
123  uint8_t *writePointer = (uint8_t *)blob + i;
124  writePointer[0] = tmp[0];
125  if (size - i > 1)
126  writePointer[1] = tmp[1];
127  }
128 
129  /* Restore I2C_EN bit */
130  pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg);
131 
132  return ret;
133 }
134 
135 void report_eeprom_error(const size_t off)
136 {
137  printk(BIOS_ERR, "MB: Failed to read from EEPROM at addr. 0x%zx\n", off);
138 }
139 
140 /*
141  * Write a single byte into the EEPROM at specified offset.
142  * Returns true on error, false on success.
143  */
144 static bool eeprom_write_byte(const uint8_t data, const uint16_t write_offset)
145 {
146  int ret = 0;
147 
148  printk(BIOS_SPEW, "CFG EEPROM: Writing %x at %x\n", data, write_offset);
149 
150  const uint32_t smb_ctrl_reg = pci_read_config32(PCH_DEV_SMBUS, HOSTC);
151  pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg | I2C_EN);
152 
153  /*
154  * The EEPROM expects two address bytes.
155  * Use the first byte of the block data as second address byte.
156  */
157  uint8_t buffer[2] = {
158  write_offset & 0xff,
159  data,
160  };
161 
162  for (size_t retry = 3; retry > 0; retry--) {
163  /* The EEPROM NACKs request when busy writing */
165  (write_offset >> 8) & 0xff,
166  sizeof(buffer), buffer);
167  if (ret == sizeof(buffer))
168  break;
169  /* Maximum of 5 milliseconds write duration */
170  mdelay(5);
171  }
172 
173  /* Restore I2C_EN bit */
174  pci_write_config32(PCH_DEV_SMBUS, HOSTC, smb_ctrl_reg);
175 
176  return ret != sizeof(buffer);
177 }
178 
179 /*
180  * Write board layout if it has changed into EEPROM.
181  * Return true on error, false on success.
182  */
183 bool write_board_settings(const struct eeprom_board_layout *new_layout)
184 {
185  const size_t off = offsetof(struct eeprom_layout, BoardLayout);
186  struct eeprom_board_layout old_layout = {0};
187  bool ret = false;
188  bool changed = false;
189 
190  /* Read old settings */
191  if (eeprom_read_buffer(&old_layout, off, sizeof(old_layout))) {
192  printk(BIOS_ERR, "CFG EEPROM: Read operation failed\n");
193  return true;
194  }
195 
196  assert(sizeof(old_layout) == sizeof(*new_layout));
197  const uint8_t *const old = (const uint8_t *)&old_layout;
198  const uint8_t *const new = (const uint8_t *)new_layout;
199 
200  /* Compare with new settings and only write changed bytes */
201  for (size_t i = 0; i < sizeof(old_layout); i++) {
202  if (old[i] != new[i]) {
203  changed = true;
204  if (eeprom_write_byte(new[i], off + i)) {
205  printk(BIOS_ERR, "CFG EEPROM: Write operation failed\n");
206  ret = true;
207  break;
208  }
209  }
210  }
211 
212  printk(BIOS_DEBUG, "CFG EEPROM: Board Layout up%s\n", changed ? "dated" : " to date");
213 
214  return ret;
215 }
#define assert(statement)
Definition: assert.h:74
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define offsetof(TYPE, MEMBER)
Definition: helpers.h:84
#define retry(attempts, condition,...)
Definition: helpers.h:126
#define printk(level,...)
Definition: stdlib.h:16
#define CRC(buf, size, crc_func)
Definition: crc_byte.h:37
uint32_t crc32_byte(uint32_t prev_crc, uint8_t data)
Definition: crc_byte.c:27
void mdelay(unsigned int msecs)
Definition: delay.c:2
bool write_board_settings(const struct eeprom_board_layout *new_layout)
Definition: eeprom.c:183
void report_eeprom_error(const size_t off)
Definition: eeprom.c:135
struct eeprom_bmc_settings * get_bmc_settings(void)
Definition: eeprom.c:73
uint8_t get_bmc_hsi(void)
Definition: eeprom.c:91
#define I2C_ADDR_EEPROM
Definition: eeprom.c:14
static bool eeprom_write_byte(const uint8_t data, const uint16_t write_offset)
Definition: eeprom.c:144
static bool get_board_settings_from_eeprom(struct eeprom_board_settings *board_cfg)
Definition: eeprom.c:40
struct eeprom_board_settings * get_board_settings(void)
Definition: eeprom.c:59
bool eeprom_read_buffer(void *blob, size_t read_offset, size_t size)
Definition: eeprom.c:103
int check_signature(const size_t offset, const uint64_t signature)
Definition: eeprom.c:20
static size_t offset
Definition: flashconsole.c:16
static __always_inline void pci_write_config32(const struct device *dev, u16 reg, u32 val)
Definition: pci_ops.h:76
static __always_inline u32 pci_read_config32(const struct device *dev, u16 reg)
Definition: pci_ops.h:58
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
static const struct mb_cfg board_cfg
Definition: romstage.c:8
u8 buffer[C2P_BUFFER_MAXSIZE]
Definition: psp_smm.c:18
int do_smbus_process_call(uintptr_t base, u8 device, u8 cmd, u16 data, u16 *buf)
Definition: smbus.c:399
int do_smbus_block_write(uintptr_t base, u8 device, u8 cmd, size_t bytes, const u8 *buf)
Definition: smbus.c:431
#define I2C_EN
Definition: smbuslib.h:12
#define SMBUS_IO_BASE
Definition: smbuslib.h:7
#define PCH_DEV_SMBUS
Definition: pci_devs.h:240
#define HOSTC
Definition: smbus.h:7
#define NULL
Definition: stddef.h:19
unsigned short uint16_t
Definition: stdint.h:11
unsigned int uint32_t
Definition: stdint.h:14
uint32_t u32
Definition: stdint.h:51
unsigned long long uint64_t
Definition: stdint.h:17
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
#define swab16(x)
Definition: swab.h:48
#define s(param, src_bits, pmcreg, dst_bits)