xref: /rk3399_rockchip-uboot/lib/avb/libavb_user/avb_ops_user.c (revision edafafc4b7ef99a12dd162f389bf9c036ad51b05)
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
25 #include <common.h>
26 #include <image.h>
27 #include <android_image.h>
28 #include <malloc.h>
29 #include <mapmem.h>
30 #include <errno.h>
31 #include <command.h>
32 #include <mmc.h>
33 #include <blk.h>
34 #include <part.h>
35 #include <stdio.h>
36 #include <android_avb/avb_ops_user.h>
37 #include <android_avb/libavb_ab.h>
38 #include <android_avb/avb_atx_validate.h>
39 #include <android_avb/avb_atx_types.h>
40 #include <optee_include/OpteeClientInterface.h>
41 #include <optee_include/tee_api_defines.h>
42 #include <android_avb/avb_vbmeta_image.h>
43 #include <android_avb/avb_atx_validate.h>
44 #include <boot_rkimg.h>
45 
46 static void byte_to_block(int64_t *offset,
47 			  size_t *num_bytes,
48 			  lbaint_t *offset_blk,
49 			  lbaint_t *blkcnt)
50 {
51 	*offset_blk = (lbaint_t)(*offset / 512);
52 	if (*num_bytes % 512 == 0) {
53 		if (*offset % 512 == 0)
54 			*blkcnt = (lbaint_t)(*num_bytes / 512);
55 		else
56 			*blkcnt = (lbaint_t)(*num_bytes / 512) + 1;
57 	} else {
58 		if (*offset % 512 == 0) {
59 			*blkcnt = (lbaint_t)(*num_bytes / 512) + 1;
60 		} else {
61 			if ((*offset % 512) + (*num_bytes % 512) < 512 ||
62 			    (*offset % 512) + (*num_bytes % 512) == 512) {
63 				*blkcnt = (lbaint_t)(*num_bytes / 512) + 1;
64 			} else {
65 				*blkcnt = (lbaint_t)(*num_bytes / 512) + 2;
66 			}
67 		}
68 	}
69 }
70 
71 static AvbIOResult get_size_of_partition(AvbOps *ops,
72 					 const char *partition,
73 					 uint64_t *out_size_in_bytes)
74 {
75 	struct blk_desc *dev_desc;
76 	disk_partition_t part_info;
77 
78 	dev_desc = rockchip_get_bootdev();
79 	if (!dev_desc) {
80 		printf("%s: Could not find device\n", __func__);
81 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
82 	}
83 
84 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
85 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
86 
87 	*out_size_in_bytes = (part_info.size) * 512;
88 	return AVB_IO_RESULT_OK;
89 }
90 
91 static AvbIOResult read_from_partition(AvbOps *ops,
92 				       const char *partition,
93 				       int64_t offset,
94 				       size_t num_bytes,
95 				       void *buffer,
96 				       size_t *out_num_read)
97 {
98 	struct blk_desc *dev_desc;
99 	lbaint_t offset_blk, blkcnt;
100 	disk_partition_t part_info;
101 	uint64_t partition_size;
102 
103 	if (offset < 0) {
104 		if (get_size_of_partition(ops, partition, &partition_size))
105 			return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
106 
107 		if (-offset > partition_size)
108 			return AVB_IO_RESULT_ERROR_RANGE_OUTSIDE_PARTITION;
109 
110 		offset = partition_size - (-offset);
111 	}
112 
113 	byte_to_block(&offset, &num_bytes, &offset_blk, &blkcnt);
114 	dev_desc = rockchip_get_bootdev();
115 	if (!dev_desc) {
116 		printf("%s: Could not find device\n", __func__);
117 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
118 	}
119 
120 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0) {
121 		printf("Could not find \"%s\" partition\n", partition);
122 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
123 	}
124 
125 	if ((offset % 512 == 0) && (num_bytes % 512 == 0)) {
126 		blk_dread(dev_desc, part_info.start + offset_blk,
127 			  blkcnt, buffer);
128 		*out_num_read = blkcnt * 512;
129 	} else {
130 		char *buffer_temp;
131 
132 		buffer_temp = malloc(512 * blkcnt);
133 		if (!buffer_temp) {
134 			printf("malloc error!\n");
135 			return AVB_IO_RESULT_ERROR_OOM;
136 		}
137 		blk_dread(dev_desc, part_info.start + offset_blk,
138 			  blkcnt, buffer_temp);
139 		memcpy(buffer, buffer_temp + (offset % 512), num_bytes);
140 		*out_num_read = num_bytes;
141 		free(buffer_temp);
142 	}
143 
144 	return AVB_IO_RESULT_OK;
145 }
146 
147 static AvbIOResult write_to_partition(AvbOps *ops,
148 				      const char *partition,
149 				      int64_t offset,
150 				      size_t num_bytes,
151 				      const void *buffer)
152 {
153 	struct blk_desc *dev_desc;
154 	char *buffer_temp;
155 	disk_partition_t part_info;
156 	lbaint_t offset_blk, blkcnt;
157 
158 	byte_to_block(&offset, &num_bytes, &offset_blk, &blkcnt);
159 	buffer_temp = malloc(512 * blkcnt);
160 	if (!buffer_temp) {
161 		printf("malloc error!\n");
162 		return AVB_IO_RESULT_ERROR_OOM;
163 	}
164 	memset(buffer_temp, 0, 512 * blkcnt);
165 	dev_desc = rockchip_get_bootdev();
166 	if (!dev_desc) {
167 		printf("%s: Could not find device\n", __func__);
168 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
169 	}
170 
171 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0) {
172 		printf("Could not find \"%s\" partition\n", partition);
173 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
174 	}
175 
176 	if ((offset % 512 != 0) && (num_bytes % 512) != 0)
177 		blk_dread(dev_desc, part_info.start + offset_blk,
178 			  blkcnt, buffer_temp);
179 
180 	memcpy(buffer_temp, buffer + (offset % 512), num_bytes);
181 	blk_dwrite(dev_desc, part_info.start + offset_blk, blkcnt, buffer);
182 	free(buffer_temp);
183 
184 	return AVB_IO_RESULT_OK;
185 }
186 
187 static AvbIOResult
188 validate_vbmeta_public_key(AvbOps *ops,
189 			   const uint8_t *public_key_data,
190 			   size_t public_key_length,
191 			   const uint8_t *public_key_metadata,
192 			   size_t public_key_metadata_length,
193 			   bool *out_is_trusted)
194 {
195 /* remain AVB_VBMETA_PUBLIC_KEY_VALIDATE to compatible legacy code */
196 #if defined(CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE) || \
197     defined(AVB_VBMETA_PUBLIC_KEY_VALIDATE)
198 	if (out_is_trusted) {
199 		avb_atx_validate_vbmeta_public_key(ops,
200 						   public_key_data,
201 						   public_key_length,
202 						   public_key_metadata,
203 						   public_key_metadata_length,
204 						   out_is_trusted);
205 	}
206 #else
207 	if (out_is_trusted)
208 		*out_is_trusted = true;
209 #endif
210 	return AVB_IO_RESULT_OK;
211 }
212 
213 static AvbIOResult read_rollback_index(AvbOps *ops,
214 				       size_t rollback_index_location,
215 				       uint64_t *out_rollback_index)
216 {
217 	if (out_rollback_index) {
218 #ifdef CONFIG_OPTEE_CLIENT
219 		int ret;
220 
221 		ret = trusty_read_rollback_index(rollback_index_location,
222 						 out_rollback_index);
223 		switch (ret) {
224 		case TEE_SUCCESS:
225 			ret = AVB_IO_RESULT_OK;
226 			break;
227 		case TEE_ERROR_GENERIC:
228 		case TEE_ERROR_NO_DATA:
229 		case TEE_ERROR_ITEM_NOT_FOUND:
230 			*out_rollback_index = 0;
231 			ret = trusty_write_rollback_index(rollback_index_location,
232 							  *out_rollback_index);
233 			if (ret) {
234 				printf("%s: init rollback index error\n",
235 				       __FILE__);
236 				ret = AVB_IO_RESULT_ERROR_IO;
237 			} else {
238 				ret =
239 				trusty_read_rollback_index(rollback_index_location,
240 							   out_rollback_index);
241 				if (ret)
242 					ret = AVB_IO_RESULT_ERROR_IO;
243 				else
244 					ret = AVB_IO_RESULT_OK;
245 			}
246 			break;
247 		default:
248 			ret = AVB_IO_RESULT_ERROR_IO;
249 			printf("%s: trusty_read_rollback_index failed",
250 			       __FILE__);
251 		}
252 
253 		return ret;
254 #else
255 		*out_rollback_index = 0;
256 
257 		return AVB_IO_RESULT_OK;
258 #endif
259 	}
260 
261 	return AVB_IO_RESULT_ERROR_IO;
262 }
263 
264 static AvbIOResult write_rollback_index(AvbOps *ops,
265 					size_t rollback_index_location,
266 					uint64_t rollback_index)
267 {
268 #ifdef CONFIG_OPTEE_CLIENT
269 	if (trusty_write_rollback_index(rollback_index_location,
270 					rollback_index)) {
271 		printf("%s: Fail to write rollback index\n", __FILE__);
272 		return AVB_IO_RESULT_ERROR_IO;
273 	}
274 	return AVB_IO_RESULT_OK;
275 #endif
276 	return AVB_IO_RESULT_ERROR_IO;
277 }
278 
279 static AvbIOResult read_is_device_unlocked(AvbOps *ops, bool *out_is_unlocked)
280 {
281 	if (out_is_unlocked) {
282 #ifdef CONFIG_OPTEE_CLIENT
283 		int ret;
284 
285 		ret = trusty_read_lock_state((uint8_t *)out_is_unlocked);
286 		switch (ret) {
287 		case TEE_SUCCESS:
288 			ret = AVB_IO_RESULT_OK;
289 			break;
290 		case TEE_ERROR_GENERIC:
291 		case TEE_ERROR_NO_DATA:
292 		case TEE_ERROR_ITEM_NOT_FOUND:
293 			*out_is_unlocked = 1;
294 			if (trusty_write_lock_state(*out_is_unlocked)) {
295 				printf("%s: init lock state error\n", __FILE__);
296 				ret = AVB_IO_RESULT_ERROR_IO;
297 			} else {
298 				ret =
299 				trusty_read_lock_state((uint8_t *)out_is_unlocked);
300 				if (ret == 0)
301 					ret = AVB_IO_RESULT_OK;
302 				else
303 					ret = AVB_IO_RESULT_ERROR_IO;
304 			}
305 			break;
306 		default:
307 			ret = AVB_IO_RESULT_ERROR_IO;
308 			printf("%s: trusty_read_lock_state failed\n", __FILE__);
309 		}
310 		return ret;
311 #else
312 		*out_is_unlocked = 1;
313 
314 		return AVB_IO_RESULT_OK;
315 #endif
316 	}
317 	return AVB_IO_RESULT_ERROR_IO;
318 }
319 
320 static AvbIOResult write_is_device_unlocked(AvbOps *ops, bool *out_is_unlocked)
321 {
322 	if (out_is_unlocked) {
323 #ifdef CONFIG_OPTEE_CLIENT
324 		if (trusty_write_lock_state(*out_is_unlocked)) {
325 			printf("%s: Fail to write lock state\n", __FILE__);
326 			return AVB_IO_RESULT_ERROR_IO;
327 		}
328 		return AVB_IO_RESULT_OK;
329 #endif
330 	}
331 	return AVB_IO_RESULT_ERROR_IO;
332 }
333 
334 static AvbIOResult get_unique_guid_for_partition(AvbOps *ops,
335 						 const char *partition,
336 						 char *guid_buf,
337 						 size_t guid_buf_size)
338 {
339 	struct blk_desc *dev_desc;
340 	disk_partition_t part_info;
341 
342 	dev_desc = rockchip_get_bootdev();
343 	if (!dev_desc) {
344 		printf("%s: Could not find device\n", __func__);
345 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
346 	}
347 
348 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0) {
349 		printf("Could not find \"%s\" partition\n", partition);
350 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
351 	}
352 	if (guid_buf && guid_buf_size > 0)
353 		memcpy(guid_buf, part_info.uuid, guid_buf_size);
354 
355 	return AVB_IO_RESULT_OK;
356 }
357 
358 /* read permanent attributes from rpmb */
359 AvbIOResult avb_read_perm_attr(AvbAtxOps *atx_ops,
360 			       AvbAtxPermanentAttributes *attributes)
361 {
362 	if (attributes) {
363 #ifdef CONFIG_OPTEE_CLIENT
364 		trusty_read_permanent_attributes((uint8_t *)attributes,
365 						 sizeof(struct AvbAtxPermanentAttributes));
366 		return AVB_IO_RESULT_OK;
367 #endif
368 	}
369 
370 	return -1;
371 }
372 
373 /*read permanent attributes hash from efuse */
374 AvbIOResult avb_read_perm_attr_hash(AvbAtxOps *atx_ops,
375 				    uint8_t hash[AVB_SHA256_DIGEST_SIZE])
376 {
377 #ifndef CONFIG_ROCKCHIP_PRELOADER_PUB_KEY
378 #ifdef CONFIG_OPTEE_CLIENT
379 	if (trusty_read_attribute_hash((uint32_t *)hash,
380 				       AVB_SHA256_DIGEST_SIZE / 4))
381 		return -1;
382 #else
383 	printf("Please open the macro!\n");
384 	return -1;
385 #endif
386 #endif
387 	return AVB_IO_RESULT_OK;
388 }
389 
390 static void avb_set_key_version(AvbAtxOps *atx_ops,
391 				size_t rollback_index_location,
392 				uint64_t key_version)
393 {
394 #ifdef CONFIG_OPTEE_CLIENT
395 	uint64_t key_version_temp = 0;
396 
397 	if (trusty_read_rollback_index(rollback_index_location, &key_version_temp))
398 		printf("%s: Fail to read rollback index\n", __FILE__);
399 	if (key_version_temp == key_version)
400 		return;
401 	if (trusty_write_rollback_index(rollback_index_location, key_version))
402 		printf("%s: Fail to write rollback index\n", __FILE__);
403 #endif
404 }
405 
406 AvbIOResult rk_get_random(AvbAtxOps *atx_ops,
407 			  size_t num_bytes,
408 			  uint8_t *output)
409 {
410 	int i;
411 	unsigned int seed;
412 
413 	seed = (unsigned int)get_timer(0);
414 	for (i = 0; i < num_bytes; i++) {
415 		srand(seed);
416 		output[i] = (uint8_t)(rand());
417 		seed = (unsigned int)(output[i]);
418 	}
419 
420 	return 0;
421 }
422 
423 #ifdef CONFIG_ANDROID_BOOT_IMAGE
424 static AvbIOResult get_preloaded_partition(AvbOps* ops,
425 					   const char* partition,
426 					   size_t num_bytes,
427 					   uint8_t** out_pointer,
428 					   size_t* out_num_bytes_preloaded,
429 					   int allow_verification_error)
430 {
431 	struct preloaded_partition *preload_info = NULL;
432 	struct AvbOpsData *data = ops->user_data;
433 	struct blk_desc *dev_desc;
434 	disk_partition_t part_info;
435 	ulong load_addr;
436 	AvbIOResult ret;
437 
438 	dev_desc = rockchip_get_bootdev();
439 	if (!dev_desc)
440 		return AVB_IO_RESULT_ERROR_IO;
441 
442 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0) {
443 		printf("Could not find \"%s\" partition\n", partition);
444 		return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
445 	}
446 
447 	if (!allow_verification_error) {
448 		if (!strncmp(partition, ANDROID_PARTITION_BOOT, 4) ||
449 		    !strncmp(partition, ANDROID_PARTITION_RECOVERY, 8))
450 			preload_info = &data->boot;
451 		else if (!strncmp(partition, ANDROID_PARTITION_VENDOR_BOOT, 11))
452 			preload_info = &data->vendor_boot;
453 		else if (!strncmp(partition, ANDROID_PARTITION_INIT_BOOT, 9))
454 			preload_info = &data->init_boot;
455 
456 		if (!preload_info) {
457 			printf("Error: unknown full load partition '%s'\n", partition);
458 			return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
459 		}
460 
461 		printf("preloaded: full image from '%s' at 0x%08lx - 0x%08lx\n",
462 		       partition, (ulong)preload_info->addr,
463 		       (ulong)preload_info->addr + num_bytes);
464 
465 		/* If the partition hasn't yet been preloaded, do it now.*/
466 		if (preload_info->size == 0) {
467 			ret = ops->read_from_partition(ops, partition,
468 						       0, num_bytes,
469 						       preload_info->addr,
470 						       &preload_info->size);
471 			if (ret != AVB_IO_RESULT_OK)
472 				return ret;
473 		}
474 		*out_pointer = preload_info->addr;
475 		*out_num_bytes_preloaded = preload_info->size;
476 		ret = AVB_IO_RESULT_OK;
477 	} else {
478 		if (!strncmp(partition, ANDROID_PARTITION_INIT_BOOT, 9) ||
479 		    !strncmp(partition, ANDROID_PARTITION_VENDOR_BOOT, 11) ||
480 		    !strncmp(partition, ANDROID_PARTITION_BOOT, 4) ||
481 		    !strncmp(partition, ANDROID_PARTITION_RECOVERY, 8)) {
482 			printf("preloaded: distribute image from '%s'\n", partition);
483 		} else {
484 			printf("Error: unknown preloaded partition '%s'\n", partition);
485 			return AVB_IO_RESULT_ERROR_OOM;
486 		}
487 
488 		/*
489 		 * Already preloaded during boot/recovery loading,
490 		 * here we just return a dummy buffer.
491 		 */
492 		if (!strncmp(partition, ANDROID_PARTITION_INIT_BOOT, 9) ||
493 		    !strncmp(partition, ANDROID_PARTITION_VENDOR_BOOT, 11)) {
494 			*out_pointer = (u8 *)avb_malloc(ARCH_DMA_MINALIGN);
495 			*out_num_bytes_preloaded = num_bytes; /* return what it expects */
496 			return AVB_IO_RESULT_OK;
497 		}
498 
499 		/*
500 		 * only boot/recovery partition can reach here
501 		 * and init/vendor_boot are loaded at this round.
502 		 */
503 		load_addr = env_get_ulong("kernel_addr_r", 16, 0);
504 		if (!load_addr)
505 			return AVB_IO_RESULT_ERROR_NO_SUCH_VALUE;
506 
507 		ret = android_image_load_by_partname(dev_desc, partition, &load_addr);
508 		if (!ret) {
509 			*out_pointer = (u8 *)load_addr;
510 			*out_num_bytes_preloaded = num_bytes; /* return what it expects */
511 			ret = AVB_IO_RESULT_OK;
512 		} else {
513 			ret = AVB_IO_RESULT_ERROR_IO;
514 		}
515 	}
516 
517 	return ret;
518 }
519 #endif
520 
521 AvbIOResult validate_public_key_for_partition(AvbOps *ops,
522 					      const char *partition,
523 					      const uint8_t *public_key_data,
524 					      size_t public_key_length,
525 					      const uint8_t *public_key_metadata,
526 					      size_t public_key_metadata_length,
527 					      bool *out_is_trusted,
528 					      uint32_t *out_rollback_index_location)
529 {
530 /* remain AVB_VBMETA_PUBLIC_KEY_VALIDATE to compatible legacy code */
531 #if defined(CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE) || \
532     defined(AVB_VBMETA_PUBLIC_KEY_VALIDATE)
533 	if (out_is_trusted) {
534 		avb_atx_validate_vbmeta_public_key(ops,
535 						   public_key_data,
536 						   public_key_length,
537 						   public_key_metadata,
538 						   public_key_metadata_length,
539 						   out_is_trusted);
540 	}
541 #else
542 	if (out_is_trusted)
543 		*out_is_trusted = true;
544 #endif
545 	*out_rollback_index_location = 0;
546 	return AVB_IO_RESULT_OK;
547 }
548 
549 AvbOps *avb_ops_user_new(void)
550 {
551 	AvbOps *ops = NULL;
552 	struct AvbOpsData *ops_data = NULL;
553 
554 	ops = calloc(1, sizeof(AvbOps));
555 	if (!ops) {
556 		printf("Error allocating memory for AvbOps.\n");
557 		goto out;
558 	}
559 	ops->ab_ops = calloc(1, sizeof(AvbABOps));
560 	if (!ops->ab_ops) {
561 		printf("Error allocating memory for AvbABOps.\n");
562 		free(ops);
563 		goto out;
564 	}
565 
566 	ops->atx_ops = calloc(1, sizeof(AvbAtxOps));
567 	if (!ops->atx_ops) {
568 		printf("Error allocating memory for AvbAtxOps.\n");
569 		free(ops->ab_ops);
570 		free(ops);
571 		goto out;
572 	}
573 
574 	ops_data = calloc(1, sizeof(struct AvbOpsData));
575 	if (!ops_data) {
576 		printf("Error allocating memory for AvbOpsData.\n");
577 		free(ops->atx_ops);
578 		free(ops->ab_ops);
579 		free(ops);
580 		goto out;
581 	}
582 
583 	ops->ab_ops->ops = ops;
584 	ops->atx_ops->ops = ops;
585 	ops_data->ops = ops;
586 	ops->user_data = ops_data;
587 
588 	ops->read_from_partition = read_from_partition;
589 	ops->write_to_partition = write_to_partition;
590 	ops->validate_vbmeta_public_key = validate_vbmeta_public_key;
591 	ops->read_rollback_index = read_rollback_index;
592 	ops->write_rollback_index = write_rollback_index;
593 	ops->read_is_device_unlocked = read_is_device_unlocked;
594 	ops->write_is_device_unlocked = write_is_device_unlocked;
595 	ops->get_unique_guid_for_partition = get_unique_guid_for_partition;
596 	ops->get_size_of_partition = get_size_of_partition;
597 #ifdef CONFIG_ANDROID_BOOT_IMAGE
598 	ops->get_preloaded_partition = get_preloaded_partition;
599 #endif
600 	ops->validate_public_key_for_partition = validate_public_key_for_partition;
601 	ops->ab_ops->read_ab_metadata = avb_ab_data_read;
602 	ops->ab_ops->write_ab_metadata = avb_ab_data_write;
603 	ops->atx_ops->read_permanent_attributes = avb_read_perm_attr;
604 	ops->atx_ops->read_permanent_attributes_hash = avb_read_perm_attr_hash;
605 	ops->atx_ops->set_key_version = avb_set_key_version;
606 	ops->atx_ops->get_random = rk_get_random;
607 
608 	return ops;
609 out:
610 	return NULL;
611 }
612 
613 void avb_ops_user_free(AvbOps *ops)
614 {
615 	free(ops->user_data);
616 	free(ops->ab_ops);
617 	free(ops->atx_ops);
618 	free(ops);
619 }
620