1 /*
2  * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stddef.h>
8 #include <string.h>
9 #include <protocols/service/smm_variable/smm_variable_proto.h>
10 #include <protocols/common/efi/efi_types.h>
11 #include "mm_communicate_serializer.h"
12 
13 /* Concrete service header serialization functions */
14 static void smm_variable_header_encode(const struct mm_communicate_serializer *serializer,
15 	uint8_t *buf, uint32_t opcode);
16 static void smm_variable_header_decode(const struct mm_communicate_serializer *serializer,
17 	uint8_t *buf, efi_status_t *efi_status);
18 
19 /**
20  * Rather than have a generic RPC header, header parameters are
21  * split between the generic MM Communicate header and an SMM
22  * service specific header that carries the function ID to
23  * call and the return status. To accommodate different
24  * service specific headers, concrete header encode/decode
25  * functions are provided, keyed off the service guid
26  * carried in the MM Communicate header.
27  */
28 struct mm_communicate_serializer
29 {
30 	/**
31 	 * \brief The service GUID
32 	 */
33 	EFI_GUID svc_guid;
34 
35 	/**
36 	 * \brief Service header size
37 	 */
38 	size_t svc_header_size;
39 
40 	/**
41 	 * \brief Service header encode function
42 	 *
43 	 * \param[in] serializer Concrete serializer
44 	 * \param[in] buf       Encode to this buffer
45 	 * \param[in] opcode    Service opcode
46 	 */
47 	void (*header_encode)(
48 		const struct mm_communicate_serializer *serializer,
49 		uint8_t *buf,
50 		uint32_t opcode);
51 
52 	/**
53 	 * \brief Header decode function
54 	 *
55 	 * \param[in] serializer Concrete serializer
56 	 * \param[in] buf       Encode to this buffer
57 	 * \param[out] efi_status EFI status code
58 	 */
59 	void (*header_decode)(
60 		const struct mm_communicate_serializer *serializer,
61 		uint8_t *buf,
62 		efi_status_t *efi_status);
63 };
64 
mm_communicate_serializer_find(const EFI_GUID * svc_guid)65 const struct mm_communicate_serializer *mm_communicate_serializer_find(const EFI_GUID *svc_guid)
66 {
67 	/* Lookup to map service guid to a concrete serializer */
68 	static const struct mm_communicate_serializer lookup[] =
69 	{
70 		/* SMM Variable mapping */
71 		{
72 			SMM_VARIABLE_GUID,
73 			SMM_VARIABLE_COMMUNICATE_HEADER_SIZE,
74 			smm_variable_header_encode,
75 			smm_variable_header_decode
76 		}
77 	};
78 
79 	/* Find a concrete mapping for the requested service */
80 	for (size_t i = 0; i < sizeof(lookup)/sizeof(struct mm_communicate_serializer); ++i) {
81 
82 		const struct mm_communicate_serializer *serializer =  &lookup[i];
83 
84 		if ((svc_guid->Data1 == serializer->svc_guid.Data1) &&
85 			(svc_guid->Data2 == serializer->svc_guid.Data2) &&
86 			(svc_guid->Data3 == serializer->svc_guid.Data3) &&
87 			(memcmp(svc_guid->Data4, serializer->svc_guid.Data4, sizeof(svc_guid->Data4)) == 0)) {
88 
89 			return serializer;
90 		}
91 	}
92 
93 	/* Failed to find serializer */
94 	return NULL;
95 }
96 
mm_communicate_serializer_header_size(const struct mm_communicate_serializer * serializer)97 size_t mm_communicate_serializer_header_size(
98 	const struct mm_communicate_serializer *serializer)
99 {
100 	return EFI_MM_COMMUNICATE_HEADER_SIZE + serializer->svc_header_size;
101 }
102 
mm_communicate_serializer_header_encode(const struct mm_communicate_serializer * serializer,uint8_t * buf,uint32_t opcode,size_t req_len)103 void mm_communicate_serializer_header_encode(
104 	const struct mm_communicate_serializer *serializer,
105 	uint8_t *buf,
106 	uint32_t opcode,
107 	size_t req_len)
108 {
109 	EFI_MM_COMMUNICATE_HEADER *hdr = (EFI_MM_COMMUNICATE_HEADER*)buf;
110 	hdr->HeaderGuid = serializer->svc_guid;
111 	hdr->MessageLength = serializer->svc_header_size + req_len;
112 
113 	/* Encode the service specific header */
114 	serializer->header_encode(serializer, hdr->Data, opcode);
115 }
116 
mm_communicate_serializer_header_decode(const struct mm_communicate_serializer * serializer,uint8_t * buf,efi_status_t * efi_status,uint8_t ** resp_buf,size_t * resp_len)117 void mm_communicate_serializer_header_decode(
118 	const struct mm_communicate_serializer *serializer,
119 	uint8_t *buf,
120 	efi_status_t *efi_status,
121 	uint8_t **resp_buf,
122 	size_t *resp_len)
123 {
124 	EFI_MM_COMMUNICATE_HEADER *hdr = (EFI_MM_COMMUNICATE_HEADER*)buf;
125 
126 	*efi_status = EFI_PROTOCOL_ERROR;
127 	*resp_len = 0;
128 
129 	if (hdr->MessageLength >= serializer->svc_header_size) {
130 
131 		*resp_len = hdr->MessageLength - serializer->svc_header_size;
132 		*resp_buf = &hdr->Data[serializer->svc_header_size];
133 
134 		/* Deserialize the service specific header */
135 		serializer->header_decode(serializer, hdr->Data, efi_status);
136 	}
137 }
138 
smm_variable_header_encode(const struct mm_communicate_serializer * serializer,uint8_t * buf,uint32_t opcode)139 static void smm_variable_header_encode(
140 	const struct mm_communicate_serializer *serializer,
141 	uint8_t *buf,
142 	uint32_t opcode)
143 {
144 	SMM_VARIABLE_COMMUNICATE_HEADER *hdr = (SMM_VARIABLE_COMMUNICATE_HEADER*)buf;
145 	hdr->Function = opcode;
146 	hdr->ReturnStatus = EFI_SUCCESS;
147 }
148 
smm_variable_header_decode(const struct mm_communicate_serializer * serializer,uint8_t * buf,efi_status_t * efi_status)149 static void smm_variable_header_decode(
150 	const struct mm_communicate_serializer *serializer,
151 	uint8_t *buf,
152 	efi_status_t *efi_status)
153 {
154 	SMM_VARIABLE_COMMUNICATE_HEADER *hdr = (SMM_VARIABLE_COMMUNICATE_HEADER*)buf;
155 	*efi_status = hdr->ReturnStatus;
156 }
157