1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
/* SPDX-License-Identifier: GPL-2.0-only */
#include <console/console.h>
#include <string.h>
#include <types.h>
#include "ec.h"
#include "mainboard.h"
#include "vpd.h"
static void write_invalid_str(char *dest, size_t length)
{
snprintf(dest, length, "%s", "INVALID");
}
const struct emi_eeprom_vpd *get_emi_eeprom_vpd(void)
{
static union {
struct emi_eeprom_vpd layout;
uint8_t raw[sizeof(struct emi_eeprom_vpd)];
} vpd = {0};
/* Check if cached VPD is valid */
if (vpd.layout.header.revision == VPD_LATEST_REVISION)
return &vpd.layout;
ec_emi_read(vpd.raw, EMI_0_IO_BASE_ADDR, 0, 0, sizeof(vpd.raw));
/* If the magic value doesn't match, consider EEPROM VPD unreliable */
if (vpd.layout.header.magic != VPD_MAGIC) {
printk(BIOS_WARNING, "Atlas VPD: Bad magic value, using fallback defaults\n");
vpd.layout.header.revision = 0;
} else {
printk(BIOS_DEBUG, "Atlas VPD: Got revision %u from EC\n",
vpd.layout.header.revision);
}
/*
* For backwards compatibility, if the VPD from the EC is an older
* version, uprev it to the latest version coreboot knows about by
* filling in the remaining fields with default values. Should the
* EC provide a newer VPD revision, coreboot would downgrade it to
* the latest version it knows about as the VPD layout is designed
* to be backwards compatible.
*
* Whenever the value of `VPD_LATEST_REVISION` is incremented, add
* a new `case` label just before the `default` label that matches
* the second latest revision to initialise the newly-added fields
* of the VPD structure with a reasonable fallback value. Note the
* intentional falling through.
*/
switch (vpd.layout.header.revision) {
case 0:
memset(vpd.raw, 0, sizeof(vpd.raw));
vpd.layout.header.magic = VPD_MAGIC;
write_invalid_str(vpd.layout.serial_number, sizeof(vpd.layout.serial_number));
write_invalid_str(vpd.layout.part_number, sizeof(vpd.layout.part_number));
vpd.layout.profile = ATLAS_PROF_UNPROGRAMMED;
__fallthrough;
default:
/* Ensure serial numbers are NULL-terminated, update revision last */
vpd.layout.serial_number[ATLAS_SN_PN_LENGTH - 1] = '\0';
vpd.layout.part_number[ATLAS_SN_PN_LENGTH - 1] = '\0';
vpd.layout.header.revision = VPD_LATEST_REVISION;
break;
}
return &vpd.layout;
}
|