summaryrefslogtreecommitdiff
path: root/src/mainboard/prodrive/atlas/vpd.c
blob: ba249a94d7a1f71cfa93a5421dceb2415d769520 (plain)
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;
}