1 /*
2 * CXL CDAT Structure
3 *
4 * Copyright (C) 2021 Avery Design Systems, Inc.
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
8 */
9
10 #include "qemu/osdep.h"
11 #include "hw/pci/pci.h"
12 #include "hw/cxl/cxl.h"
13 #include "qapi/error.h"
14 #include "qemu/error-report.h"
15
cdat_len_check(CDATSubHeader * hdr,Error ** errp)16 static void cdat_len_check(CDATSubHeader *hdr, Error **errp)
17 {
18 assert(hdr->length);
19 assert(hdr->reserved == 0);
20
21 switch (hdr->type) {
22 case CDAT_TYPE_DSMAS:
23 assert(hdr->length == sizeof(CDATDsmas));
24 break;
25 case CDAT_TYPE_DSLBIS:
26 assert(hdr->length == sizeof(CDATDslbis));
27 break;
28 case CDAT_TYPE_DSMSCIS:
29 assert(hdr->length == sizeof(CDATDsmscis));
30 break;
31 case CDAT_TYPE_DSIS:
32 assert(hdr->length == sizeof(CDATDsis));
33 break;
34 case CDAT_TYPE_DSEMTS:
35 assert(hdr->length == sizeof(CDATDsemts));
36 break;
37 case CDAT_TYPE_SSLBIS:
38 assert(hdr->length >= sizeof(CDATSslbisHeader));
39 assert((hdr->length - sizeof(CDATSslbisHeader)) %
40 sizeof(CDATSslbe) == 0);
41 break;
42 default:
43 error_setg(errp, "Type %d is reserved", hdr->type);
44 }
45 }
46
ct3_build_cdat(CDATObject * cdat,Error ** errp)47 static bool ct3_build_cdat(CDATObject *cdat, Error **errp)
48 {
49 g_autofree CDATTableHeader *cdat_header = NULL;
50 g_autofree CDATEntry *cdat_st = NULL;
51 uint8_t sum = 0;
52 uint8_t *hdr_buf;
53 int ent, i;
54
55 /* Use default table if fopen == NULL */
56 assert(cdat->build_cdat_table);
57
58 cdat_header = g_malloc0(sizeof(*cdat_header));
59 if (!cdat_header) {
60 error_setg(errp, "Failed to allocate CDAT header");
61 return false;
62 }
63
64 cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf,
65 cdat->private);
66
67 if (cdat->built_buf_len <= 0) {
68 /* Build later as not all data available yet */
69 cdat->to_update = true;
70 return true;
71 }
72 cdat->to_update = false;
73
74 cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1));
75 if (!cdat_st) {
76 error_setg(errp, "Failed to allocate CDAT entry array");
77 return false;
78 }
79
80 /* Entry 0 for CDAT header, starts with Entry 1 */
81 for (ent = 1; ent < cdat->built_buf_len + 1; ent++) {
82 CDATSubHeader *hdr = cdat->built_buf[ent - 1];
83 uint8_t *buf = (uint8_t *)cdat->built_buf[ent - 1];
84
85 cdat_st[ent].base = hdr;
86 cdat_st[ent].length = hdr->length;
87
88 cdat_header->length += hdr->length;
89 for (i = 0; i < hdr->length; i++) {
90 sum += buf[i];
91 }
92 }
93
94 /* CDAT header */
95 cdat_header->revision = CXL_CDAT_REV;
96 /* For now, no runtime updates */
97 cdat_header->sequence = 0;
98 cdat_header->length += sizeof(CDATTableHeader);
99
100 hdr_buf = (uint8_t *)cdat_header;
101 for (i = 0; i < sizeof(*cdat_header); i++) {
102 sum += hdr_buf[i];
103 }
104
105 /* Sum of all bytes including checksum must be 0 */
106 cdat_header->checksum = ~sum + 1;
107
108 cdat_st[0].base = g_steal_pointer(&cdat_header);
109 cdat_st[0].length = sizeof(*cdat_header);
110 cdat->entry_len = 1 + cdat->built_buf_len;
111 cdat->entry = g_steal_pointer(&cdat_st);
112 return true;
113 }
114
ct3_load_cdat(CDATObject * cdat,Error ** errp)115 static bool ct3_load_cdat(CDATObject *cdat, Error **errp)
116 {
117 g_autofree CDATEntry *cdat_st = NULL;
118 g_autofree uint8_t *buf = NULL;
119 uint8_t sum = 0;
120 int num_ent;
121 int i = 0, ent = 1;
122 gsize file_size = 0;
123 CDATSubHeader *hdr;
124 GError *error = NULL;
125
126 /* Read CDAT file and create its cache */
127 if (!g_file_get_contents(cdat->filename, (gchar **)&buf,
128 &file_size, &error)) {
129 error_setg(errp, "CDAT: File read failed: %s", error->message);
130 g_error_free(error);
131 return false;
132 }
133 if (file_size < sizeof(CDATTableHeader)) {
134 error_setg(errp, "CDAT: File too short");
135 return false;
136 }
137 i = sizeof(CDATTableHeader);
138 num_ent = 1;
139 while (i < file_size) {
140 hdr = (CDATSubHeader *)(buf + i);
141 if (i + sizeof(CDATSubHeader) > file_size) {
142 error_setg(errp, "CDAT: Truncated table");
143 return false;
144 }
145 cdat_len_check(hdr, errp);
146 i += hdr->length;
147 if (i > file_size) {
148 error_setg(errp, "CDAT: Truncated table");
149 return false;
150 }
151 num_ent++;
152 }
153 if (i != file_size) {
154 error_setg(errp, "CDAT: File length mismatch");
155 return false;
156 }
157
158 cdat_st = g_new0(CDATEntry, num_ent);
159
160 /* Set CDAT header, Entry = 0 */
161 cdat_st[0].base = buf;
162 cdat_st[0].length = sizeof(CDATTableHeader);
163 i = 0;
164
165 while (i < cdat_st[0].length) {
166 sum += buf[i++];
167 }
168
169 /* Read CDAT structures */
170 while (i < file_size) {
171 hdr = (CDATSubHeader *)(buf + i);
172 cdat_st[ent].base = hdr;
173 cdat_st[ent].length = hdr->length;
174
175 while (buf + i < (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) {
176 assert(i < file_size);
177 sum += buf[i++];
178 }
179
180 ent++;
181 }
182
183 if (sum != 0) {
184 warn_report("CDAT: Found checksum mismatch in %s", cdat->filename);
185 }
186 cdat->entry_len = num_ent;
187 cdat->entry = g_steal_pointer(&cdat_st);
188 cdat->buf = g_steal_pointer(&buf);
189 return true;
190 }
191
cxl_doe_cdat_init(CXLComponentState * cxl_cstate,Error ** errp)192 bool cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp)
193 {
194 CDATObject *cdat = &cxl_cstate->cdat;
195
196 if (cdat->filename) {
197 return ct3_load_cdat(cdat, errp);
198 } else {
199 return ct3_build_cdat(cdat, errp);
200 }
201 }
202
cxl_doe_cdat_update(CXLComponentState * cxl_cstate,Error ** errp)203 void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp)
204 {
205 CDATObject *cdat = &cxl_cstate->cdat;
206
207 if (cdat->to_update) {
208 ct3_build_cdat(cdat, errp);
209 }
210 }
211
cxl_doe_cdat_release(CXLComponentState * cxl_cstate)212 void cxl_doe_cdat_release(CXLComponentState *cxl_cstate)
213 {
214 CDATObject *cdat = &cxl_cstate->cdat;
215
216 free(cdat->entry);
217 if (cdat->built_buf) {
218 cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len,
219 cdat->private);
220 }
221 g_free(cdat->buf);
222 }
223