summaryrefslogtreecommitdiff
path: root/chip/g/sn_bits.c
blob: fee2a6d91ba12f3daf4cecb97f9dcb8e94eb44b6 (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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/* Copyright 2018 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "board_id.h"
#include "board_space.h"
#include "console.h"
#include "extension.h"
#include "flash_info.h"
#include "util.h"
#include "wp.h"

#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)

int read_sn_data(struct sn_data *sn)
{
	uint32_t *id_p;
	int i;

	/*
	 * SN Bits structure size is guaranteed to be divisible by 4, and it
	 * is guaranteed to be aligned at 4 bytes.
	 */

	id_p = (uint32_t *)sn;

	for (i = 0; i < sizeof(*sn); i += sizeof(uint32_t)) {
		int rv;

		rv = flash_physical_info_read_word
			(INFO_SN_DATA_OFFSET + i, id_p);
		if (rv != EC_SUCCESS) {
			CPRINTF("%s: failed to read word %d, error %d\n",
				__func__, i, rv);
			return rv;
		}
		id_p++;
	}
	return EC_SUCCESS;
}

static int write_sn_data(struct sn_data *sn_data, int header_only)
{
	int rv = EC_SUCCESS;

	/* Enable write access */
	flash_info_write_enable();

	/* Write sn bits */
	rv = flash_info_physical_write(INFO_SN_DATA_OFFSET,
				       header_only ?
				       SN_HEADER_SIZE : sizeof(*sn_data),
				       (const char *)sn_data);
	if (rv != EC_SUCCESS)
		CPRINTS("%s: write failed", __func__);

	/* Disable write access */
	flash_info_write_disable();

	return rv;
}

/**
 * Initialize SN data space in flash INFO1, and write sn hash. This can only
 * be called once per device; subsequent calls on a device that has already
 * had the sn hash written will fail.
 *
 * @param id	Pointer to a SN  structure to copy into INFO1
 *
 * @return EC_SUCCESS or an error code in cases of various failures to read or
 *		if the space has been already initialized.
 */
static int write_sn_hash(const uint32_t sn_hash[3])
{
	int rv = EC_ERROR_PARAM_COUNT;
	int i;
	struct sn_data sn_data;

	rv = read_sn_data(&sn_data);
	if (rv != EC_SUCCESS)
		return rv;

	/* Check the sn data space is currently uninitialized */
	for (i = 0; i < (sizeof(sn_data) / sizeof(uint32_t)); i++)
		if (((uint32_t *) &sn_data)[i] != 0xffffffff)
			return EC_ERROR_INVALID_CONFIG;

	sn_data.version = SN_DATA_VERSION;
	memcpy(sn_data.sn_hash, sn_hash, sizeof(sn_data.sn_hash));

	rv = write_sn_data(&sn_data, 0);

	return rv;
}

static int increment_rma_count(uint8_t inc)
{
	int rv = EC_ERROR_PARAM_COUNT;
	struct sn_data sn_data;

	rv = read_sn_data(&sn_data);
	if (rv != EC_SUCCESS)
		return rv;

	/* Make sure we know how to update this data */
	if (sn_data.version != SN_DATA_VERSION)
		return EC_ERROR_INVALID_CONFIG;

	/* Don't allow incrementing more than the number of bits */
	if (inc > RMA_COUNT_BITS)
		return EC_ERROR_INVAL;

	/*
	 * The RMA status is initially set to 0xff. We set bit 7
	 * to 0 to indicate the device has been RMA'd at least once,
	 * and use the remaining bits as a count of how many times
	 * the device has been RMA'd. The number of 0s represents
	 * the number of RMAs. As there are only 7 bits available
	 * for the count, a value of 0x00 means the device has
	 * been RMA'd at least 7 times (but we do not know how many).
	 *
	 * We allow incrementing by 0 or n (rather than 0 or 1) so
	 * that a device in any state can be put into the RMA'd with
	 * unknown count (0x00) state with a single call to this
	 * function.
	 */
	sn_data.rma_status <<= inc;
	sn_data.rma_status &= RMA_INDICATOR;

	rv = write_sn_data(&sn_data, 1);

	return rv;
}

static enum vendor_cmd_rc vc_sn_set_hash(enum vendor_cmd_cc code,
					 void *buf,
					 size_t input_size,
					 size_t *response_size)
{
	struct board_id bid;
	uint32_t sn_hash[3];
	uint8_t *pbuf = buf;

	*response_size = 1;

	if (input_size != sizeof(sn_hash)) {
		*pbuf = VENDOR_RC_BOGUS_ARGS;
		return VENDOR_RC_BOGUS_ARGS;
	}

	/*
	 * Only allow writing sn bits if we can successfully verify
	 * that the board ID type has not been writen yet.
	 */
	if (read_board_id(&bid) != EC_SUCCESS ||
	    !board_id_type_is_blank(&bid)) {
		*pbuf = EC_ERROR_ACCESS_DENIED;
		return VENDOR_RC_NOT_ALLOWED;
	}

	memcpy(&sn_hash, pbuf, sizeof(sn_hash));

	/* We care about the LSB only. */
	*pbuf = (uint8_t) write_sn_hash(sn_hash);

	return *pbuf;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_SN_SET_HASH, vc_sn_set_hash);

static enum vendor_cmd_rc vc_sn_inc_rma(enum vendor_cmd_cc code,
					void *buf,
					size_t input_size,
					size_t *response_size)
{
	uint8_t *pbuf = buf;

	if (wp_is_asserted())
		return EC_ERROR_ACCESS_DENIED;

	*response_size = 1;

	if (input_size != sizeof(*pbuf)) {
		*pbuf = VENDOR_RC_BOGUS_ARGS;
		return VENDOR_RC_BOGUS_ARGS;
	}

	/* We care about the LSB only. */
	*pbuf = (uint8_t) increment_rma_count(*pbuf);

	return *pbuf;
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_SN_INC_RMA, vc_sn_inc_rma);

static int command_sn(int argc, char **argv)
{
	int rv = EC_ERROR_PARAM_COUNT;
	struct sn_data sn;

	switch (argc) {
#ifdef CR50_DEV
	case 4:
	{
		char *e;

		sn.sn_hash[0] = strtoi(argv[1], &e, 0);
		if (*e)
			return EC_ERROR_PARAM1;

		sn.sn_hash[1] = strtoi(argv[2], &e, 0);
		if (*e)
			return EC_ERROR_PARAM2;

		sn.sn_hash[2] = strtoi(argv[3], &e, 0);
		if (*e)
			return EC_ERROR_PARAM3;

		rv = write_sn_hash(sn.sn_hash);
		if (rv != EC_SUCCESS)
			return rv;

		goto print_sn_data;
	}
	case 3:
	{
		int count;
		char *e;

		if (strcasecmp(argv[1], "rmainc") != 0)
			return EC_ERROR_PARAM1;

		count = strtoi(argv[2], &e, 0);
		if (*e || count > 7)
			return EC_ERROR_PARAM2;

		rv = increment_rma_count(count);
		if (rv != EC_SUCCESS)
			return rv;
	}
	/* fall through */
print_sn_data:
#endif
	case 1:
		rv = read_sn_data(&sn);
		if (rv == EC_SUCCESS)
			CPRINTF("Version: %02x\n"
				"RMA: %02x\n"
				"SN: %08x %08x %08x\n",
				sn.version, sn.rma_status,
				sn.sn_hash[0], sn.sn_hash[1], sn.sn_hash[2]);

		break;
	default:
		rv = EC_ERROR_PARAM_COUNT;
	}

	return rv;
}
DECLARE_SAFE_CONSOLE_COMMAND(sn,
			     command_sn, ""
#ifdef CR50_DEV
			     "[(sn0 sn1 sn2) | (rmainc n)]"
#endif
			     , "Get"
#ifdef CR50_DEV
			     "/Set"
#endif
			     " Serial Number Data");