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