1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2022, Linaro Limited
4  */
5 
6 #define LOG_CATEGORY UCLASS_FWU_MDATA
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <efi_loader.h>
11 #include <fwu.h>
12 #include <fwu_mdata.h>
13 #include <log.h>
14 
15 #include <linux/errno.h>
16 #include <linux/types.h>
17 #include <u-boot/crc.h>
18 
19 /**
20  * fwu_get_mdata_part_num() - Get the FWU metadata partition numbers
21  * @dev: FWU metadata device
22  * @mdata_parts: array for storing the metadata partition numbers
23  *
24  * Get the partition numbers on the storage device on which the
25  * FWU metadata is stored. Two partition numbers will be returned.
26  *
27  * Return: 0 if OK, -ve on error
28  *
29  */
fwu_get_mdata_part_num(struct udevice * dev,uint * mdata_parts)30 int fwu_get_mdata_part_num(struct udevice *dev, uint *mdata_parts)
31 {
32 	const struct fwu_mdata_ops *ops = device_get_ops(dev);
33 
34 	if (!ops->get_mdata_part_num) {
35 		log_debug("get_mdata_part_num() method not defined\n");
36 		return -ENOSYS;
37 	}
38 
39 	return ops->get_mdata_part_num(dev, mdata_parts);
40 }
41 
42 /**
43  * fwu_read_mdata_partition() - Read the FWU metadata from a partition
44  * @dev: FWU metadata device
45  * @mdata: Copy of the FWU metadata
46  * @part_num: Partition number from which FWU metadata is to be read
47  *
48  * Read the FWU metadata from the specified partition number
49  *
50  * Return: 0 if OK, -ve on error
51  *
52  */
fwu_read_mdata_partition(struct udevice * dev,struct fwu_mdata * mdata,uint part_num)53 int fwu_read_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
54 			     uint part_num)
55 {
56 	const struct fwu_mdata_ops *ops = device_get_ops(dev);
57 
58 	if (!ops->read_mdata_partition) {
59 		log_debug("read_mdata_partition() method not defined\n");
60 		return -ENOSYS;
61 	}
62 
63 	return ops->read_mdata_partition(dev, mdata, part_num);
64 }
65 
66 /**
67  * fwu_write_mdata_partition() - Write the FWU metadata to a partition
68  * @dev: FWU metadata device
69  * @mdata: Copy of the FWU metadata
70  * @part_num: Partition number to which FWU metadata is to be written
71  *
72  * Write the FWU metadata to the specified partition number
73  *
74  * Return: 0 if OK, -ve on error
75  *
76  */
fwu_write_mdata_partition(struct udevice * dev,struct fwu_mdata * mdata,uint part_num)77 int fwu_write_mdata_partition(struct udevice *dev, struct fwu_mdata *mdata,
78 			      uint part_num)
79 {
80 	const struct fwu_mdata_ops *ops = device_get_ops(dev);
81 
82 	if (!ops->write_mdata_partition) {
83 		log_debug("write_mdata_partition() method not defined\n");
84 		return -ENOSYS;
85 	}
86 
87 	return ops->write_mdata_partition(dev, mdata, part_num);
88 }
89 
90 /**
91  * fwu_mdata_check() - Check if the FWU metadata is valid
92  * @dev: FWU metadata device
93  *
94  * Validate both copies of the FWU metadata. If one of the copies
95  * has gone bad, restore it from the other copy.
96  *
97  * Return: 0 if OK, -ve on error
98  *
99  */
fwu_mdata_check(struct udevice * dev)100 int fwu_mdata_check(struct udevice *dev)
101 {
102 	const struct fwu_mdata_ops *ops = device_get_ops(dev);
103 
104 	if (!ops->check_mdata) {
105 		log_debug("check_mdata() method not defined\n");
106 		return -ENOSYS;
107 	}
108 
109 	return ops->check_mdata(dev);
110 }
111 
112 /**
113  * fwu_get_mdata() - Get a FWU metadata copy
114  * @dev: FWU metadata device
115  * @mdata: Copy of the FWU metadata
116  *
117  * Get a valid copy of the FWU metadata.
118  *
119  * Note: This function is to be called first when modifying any fields
120  * in the metadata. The sequence of calls to modify any field in the
121  * metadata would  be 1) fwu_get_mdata 2) Modify metadata, followed by
122  * 3) fwu_update_mdata
123  *
124  * Return: 0 if OK, -ve on error
125  *
126  */
fwu_get_mdata(struct udevice * dev,struct fwu_mdata * mdata)127 int fwu_get_mdata(struct udevice *dev, struct fwu_mdata *mdata)
128 {
129 	const struct fwu_mdata_ops *ops = device_get_ops(dev);
130 
131 	if (!ops->get_mdata) {
132 		log_debug("get_mdata() method not defined\n");
133 		return -ENOSYS;
134 	}
135 
136 	return ops->get_mdata(dev, mdata);
137 }
138 
139 /**
140  * fwu_update_mdata() - Update the FWU metadata
141  * @dev: FWU metadata device
142  * @mdata: Copy of the FWU metadata
143  *
144  * Update the FWU metadata structure by writing to the
145  * FWU metadata partitions.
146  *
147  * Note: This function is not to be called directly to update the
148  * metadata fields. The sequence of function calls should be
149  * 1) fwu_get_mdata() 2) Modify the medata fields 3) fwu_update_mdata()
150  *
151  * The sequence of updating the partitions should be, update the
152  * primary metadata partition (first partition encountered), followed
153  * by updating the secondary partition. With this update sequence, in
154  * the rare scenario that the two metadata partitions are valid but do
155  * not match, maybe due to power outage at the time of updating the
156  * metadata copies, the secondary partition can be updated from the
157  * primary.
158  *
159  * Return: 0 if OK, -ve on error
160  *
161  */
fwu_update_mdata(struct udevice * dev,struct fwu_mdata * mdata)162 int fwu_update_mdata(struct udevice *dev, struct fwu_mdata *mdata)
163 {
164 	void *buf;
165 	const struct fwu_mdata_ops *ops = device_get_ops(dev);
166 
167 	if (!ops->update_mdata) {
168 		log_debug("get_mdata() method not defined\n");
169 		return -ENOSYS;
170 	}
171 
172 	/*
173 	 * Calculate the crc32 for the updated FWU metadata
174 	 * and put the updated value in the FWU metadata crc32
175 	 * field
176 	 */
177 	buf = &mdata->version;
178 	mdata->crc32 = crc32(0, buf, sizeof(*mdata) - sizeof(u32));
179 
180 	return ops->update_mdata(dev, mdata);
181 }
182 
183 UCLASS_DRIVER(fwu_mdata) = {
184 	.id		= UCLASS_FWU_MDATA,
185 	.name		= "fwu-mdata",
186 };
187