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 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 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 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 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 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 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