summaryrefslogtreecommitdiff
path: root/util/nvmutil/lib/checksum.c
blob: 35b88eb95fd63257796e738e3d6f0fd6210beea8 (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
/* SPDX-License-Identifier: MIT
 *
 * Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
 *
 * Functions related to GbE NVM checksums.
 *
 * Related file: word.c
 */

#ifdef __OpenBSD__
#include <sys/param.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "../include/common.h"

void
read_checksums(void)
{
	struct xstate *x = xstatus();
	struct commands *cmd;
	struct xfile *f;

	unsigned long _p;
	unsigned long _skip_part;

	unsigned char _num_invalid;
	unsigned char _max_invalid;

	cmd = &x->cmd[x->i];
	f = &x->f;

	f->part_valid[0] = 0;
	f->part_valid[1] = 0;

	if (!cmd->chksum_read)
		return;

	_num_invalid = 0;
	_max_invalid = 2;

	if (cmd->arg_part)
		_max_invalid = 1;

	/*
	 * Skip verification on this part,
	 * but only when arg_part is set.
	 */
	_skip_part = f->part ^ 1;

	for (_p = 0; _p < 2; _p++) {
		/*
		 * Only verify a part if it was *read*
		 */
		if (cmd->arg_part && (_p == _skip_part))
			continue;

		f->part_valid[_p] = good_checksum(_p);
		if (!f->part_valid[_p])
			++_num_invalid;
	}

	if (_num_invalid >= _max_invalid) {
		if (_max_invalid == 1)
			err(ECANCELED, "%s: part %lu has a bad checksum",
			    f->fname, (unsigned long)f->part);
		err(ECANCELED, "%s: No valid checksum found in file",
		    f->fname);
	}
}

int
good_checksum(unsigned long partnum)
{
	unsigned short expected_checksum;
	unsigned short actual_checksum;

	expected_checksum =
	    calculated_checksum(partnum);

	actual_checksum =
	    nvm_word(NVM_CHECKSUM_WORD, partnum);

	if (expected_checksum == actual_checksum) {
		return 1;
	} else {
		return 0;
	}
}

void
set_checksum(unsigned long p)
{
	check_bin(p, "part number");
	set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
}

unsigned short
calculated_checksum(unsigned long p)
{
	unsigned long c;
	unsigned int val16;

	val16 = 0;

	for (c = 0; c < NVM_CHECKSUM_WORD; c++)
		val16 += (unsigned int)nvm_word(c, p);

	return (unsigned short)((NVM_CHECKSUM - val16) & 0xffff);
}