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 void 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 int ent, i; 53 54 /* Use default table if fopen == NULL */ 55 assert(cdat->build_cdat_table); 56 57 cdat_header = g_malloc0(sizeof(*cdat_header)); 58 if (!cdat_header) { 59 error_setg(errp, "Failed to allocate CDAT header"); 60 return; 61 } 62 63 cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private); 64 65 if (!cdat->built_buf_len) { 66 /* Build later as not all data available yet */ 67 cdat->to_update = true; 68 return; 69 } 70 cdat->to_update = false; 71 72 cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1)); 73 if (!cdat_st) { 74 error_setg(errp, "Failed to allocate CDAT entry array"); 75 return; 76 } 77 78 /* Entry 0 for CDAT header, starts with Entry 1 */ 79 for (ent = 1; ent < cdat->built_buf_len + 1; ent++) { 80 CDATSubHeader *hdr = cdat->built_buf[ent - 1]; 81 uint8_t *buf = (uint8_t *)cdat->built_buf[ent - 1]; 82 83 cdat_st[ent].base = hdr; 84 cdat_st[ent].length = hdr->length; 85 86 cdat_header->length += hdr->length; 87 for (i = 0; i < hdr->length; i++) { 88 sum += buf[i]; 89 } 90 } 91 92 /* CDAT header */ 93 cdat_header->revision = CXL_CDAT_REV; 94 /* For now, no runtime updates */ 95 cdat_header->sequence = 0; 96 cdat_header->length += sizeof(CDATTableHeader); 97 sum += cdat_header->revision + cdat_header->sequence + 98 cdat_header->length; 99 /* Sum of all bytes including checksum must be 0 */ 100 cdat_header->checksum = ~sum + 1; 101 102 cdat_st[0].base = g_steal_pointer(&cdat_header); 103 cdat_st[0].length = sizeof(*cdat_header); 104 cdat->entry_len = 1 + cdat->built_buf_len; 105 cdat->entry = g_steal_pointer(&cdat_st); 106 } 107 108 static void ct3_load_cdat(CDATObject *cdat, Error **errp) 109 { 110 g_autofree CDATEntry *cdat_st = NULL; 111 g_autofree char *buf = NULL; 112 uint8_t sum = 0; 113 int num_ent; 114 int i = 0, ent = 1; 115 gsize file_size = 0; 116 CDATSubHeader *hdr; 117 GError *error = NULL; 118 119 /* Read CDAT file and create its cache */ 120 if (!g_file_get_contents(cdat->filename, (gchar **)&buf, 121 &file_size, &error)) { 122 error_setg(errp, "CDAT: File read failed: %s", error->message); 123 g_error_free(error); 124 return; 125 } 126 if (file_size < sizeof(CDATTableHeader)) { 127 error_setg(errp, "CDAT: File too short"); 128 return; 129 } 130 i = sizeof(CDATTableHeader); 131 num_ent = 1; 132 while (i < file_size) { 133 hdr = (CDATSubHeader *)(buf + i); 134 if (i + sizeof(CDATSubHeader) > file_size) { 135 error_setg(errp, "CDAT: Truncated table"); 136 return; 137 } 138 cdat_len_check(hdr, errp); 139 i += hdr->length; 140 if (i > file_size) { 141 error_setg(errp, "CDAT: Truncated table"); 142 return; 143 } 144 num_ent++; 145 } 146 if (i != file_size) { 147 error_setg(errp, "CDAT: File length mismatch"); 148 return; 149 } 150 151 cdat_st = g_new0(CDATEntry, num_ent); 152 153 /* Set CDAT header, Entry = 0 */ 154 cdat_st[0].base = buf; 155 cdat_st[0].length = sizeof(CDATTableHeader); 156 i = 0; 157 158 while (i < cdat_st[0].length) { 159 sum += buf[i++]; 160 } 161 162 /* Read CDAT structures */ 163 while (i < file_size) { 164 hdr = (CDATSubHeader *)(buf + i); 165 cdat_st[ent].base = hdr; 166 cdat_st[ent].length = hdr->length; 167 168 while (buf + i < (char *)cdat_st[ent].base + cdat_st[ent].length) { 169 assert(i < file_size); 170 sum += buf[i++]; 171 } 172 173 ent++; 174 } 175 176 if (sum != 0) { 177 warn_report("CDAT: Found checksum mismatch in %s", cdat->filename); 178 } 179 cdat->entry_len = num_ent; 180 cdat->entry = g_steal_pointer(&cdat_st); 181 cdat->buf = g_steal_pointer(&buf); 182 } 183 184 void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) 185 { 186 CDATObject *cdat = &cxl_cstate->cdat; 187 188 if (cdat->filename) { 189 ct3_load_cdat(cdat, errp); 190 } else { 191 ct3_build_cdat(cdat, errp); 192 } 193 } 194 195 void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp) 196 { 197 CDATObject *cdat = &cxl_cstate->cdat; 198 199 if (cdat->to_update) { 200 ct3_build_cdat(cdat, errp); 201 } 202 } 203 204 void cxl_doe_cdat_release(CXLComponentState *cxl_cstate) 205 { 206 CDATObject *cdat = &cxl_cstate->cdat; 207 208 free(cdat->entry); 209 if (cdat->built_buf) { 210 cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len, 211 cdat->private); 212 } 213 g_free(cdat->buf); 214 } 215