1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Support for booting from coreboot
4  *
5  * Copyright 2021 Google LLC
6  */
7 
8 #define LOG_CATEGORY	UCLASS_RTC
9 
10 #include <command.h>
11 #include <dm.h>
12 #include <rtc.h>
13 #include <asm/cb_sysinfo.h>
14 #include <asm/global_data.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
get_table(void)18 const struct sysinfo_t *get_table(void)
19 {
20 	if (!gd->arch.coreboot_table) {
21 		printf("No coreboot sysinfo table found\n");
22 		return NULL;
23 	}
24 
25 	return &lib_sysinfo;
26 }
27 
calc_sum(struct udevice * dev,uint start_bit,uint bit_count)28 static int calc_sum(struct udevice *dev, uint start_bit, uint bit_count)
29 {
30 	uint start_byte = start_bit / 8;
31 	uint byte_count = bit_count / 8;
32 	int ret, i;
33 	uint sum;
34 
35 	log_debug("Calc sum from %x: %x bytes\n", start_byte, byte_count);
36 	sum = 0;
37 	for (i = 0; i < bit_count / 8; i++) {
38 		ret = rtc_read8(dev, start_bit / 8 + i);
39 		if (ret < 0)
40 			return ret;
41 		sum += ret;
42 	}
43 
44 	return (sum & 0xff) << 8 | (sum & 0xff00) >> 8;
45 }
46 
47 /**
48  * prep_cbcmos() - Prepare for a CMOS-RAM command
49  *
50  * @tab: coreboot table
51  * @devnum: RTC device name to use, or NULL for the first one
52  * @dep: Returns RTC device on success
53  * Return: calculated checksum for CMOS RAM or -ve on error
54  */
prep_cbcmos(const struct sysinfo_t * tab,const char * devname,struct udevice ** devp)55 static int prep_cbcmos(const struct sysinfo_t *tab, const char *devname,
56 		       struct udevice **devp)
57 {
58 	struct udevice *dev;
59 	int ret;
60 
61 	if (!tab)
62 		return CMD_RET_FAILURE;
63 	if (devname)
64 		ret = uclass_get_device_by_name(UCLASS_RTC, devname, &dev);
65 	else
66 		ret = uclass_first_device_err(UCLASS_RTC, &dev);
67 	if (ret) {
68 		printf("Failed to get RTC device: %dE\n", ret);
69 		return ret;
70 	}
71 
72 	ret = calc_sum(dev, tab->cmos_range_start,
73 		       tab->cmos_range_end + 1 - tab->cmos_range_start);
74 	if (ret < 0) {
75 		printf("Failed to read RTC device: %dE\n", ret);
76 		return ret;
77 	}
78 	*devp = dev;
79 
80 	return ret;
81 }
82 
do_cbcmos_check(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])83 static int do_cbcmos_check(struct cmd_tbl *cmdtp, int flag, int argc,
84 			   char *const argv[])
85 {
86 	const struct sysinfo_t *tab = get_table();
87 	struct udevice *dev;
88 	u16 cur, sum;
89 	int ret;
90 
91 	ret = prep_cbcmos(tab, argv[1], &dev);
92 	if (ret < 0)
93 		return CMD_RET_FAILURE;
94 	sum = ret;
95 
96 	ret = rtc_read16(dev, tab->cmos_checksum_location / 8, &cur);
97 	if (ret < 0) {
98 		printf("Failed to read RTC device: %dE\n", ret);
99 		return CMD_RET_FAILURE;
100 	}
101 	if (sum != cur) {
102 		printf("Checksum %04x error: calculated %04x\n", cur, sum);
103 		return CMD_RET_FAILURE;
104 	}
105 
106 	return 0;
107 }
108 
do_cbcmos_update(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])109 static int do_cbcmos_update(struct cmd_tbl *cmdtp, int flag, int argc,
110 			    char *const argv[])
111 {
112 	const struct sysinfo_t *tab = get_table();
113 	struct udevice *dev;
114 	u16 sum;
115 	int ret;
116 
117 	ret = prep_cbcmos(tab, argv[1], &dev);
118 	if (ret < 0)
119 		return CMD_RET_FAILURE;
120 	sum = ret;
121 
122 	ret = rtc_write16(dev, tab->cmos_checksum_location / 8, sum);
123 	if (ret < 0) {
124 		printf("Failed to read RTC device: %dE\n", ret);
125 		return CMD_RET_FAILURE;
126 	}
127 	printf("Checksum %04x written\n", sum);
128 
129 	return 0;
130 }
131 
132 U_BOOT_LONGHELP(cbcmos,
133 	"check     - check CMOS RAM\n"
134 	"cbcmos update    - Update CMOS-RAM checksum";
135 );
136 
137 U_BOOT_CMD_WITH_SUBCMDS(cbcmos, "coreboot CMOS RAM", cbcmos_help_text,
138 	U_BOOT_SUBCMD_MKENT(check, 2, 1, do_cbcmos_check),
139 	U_BOOT_SUBCMD_MKENT(update, 2, 1, do_cbcmos_update));
140