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 uint8_t sum = 0; 112 int num_ent; 113 int i = 0, ent = 1, file_size = 0; 114 CDATSubHeader *hdr; 115 FILE *fp = NULL; 116 117 /* Read CDAT file and create its cache */ 118 fp = fopen(cdat->filename, "r"); 119 if (!fp) { 120 error_setg(errp, "CDAT: Unable to open file"); 121 return; 122 } 123 124 fseek(fp, 0, SEEK_END); 125 file_size = ftell(fp); 126 fseek(fp, 0, SEEK_SET); 127 cdat->buf = g_malloc0(file_size); 128 129 if (fread(cdat->buf, file_size, 1, fp) == 0) { 130 error_setg(errp, "CDAT: File read failed"); 131 return; 132 } 133 134 fclose(fp); 135 136 if (file_size < sizeof(CDATTableHeader)) { 137 error_setg(errp, "CDAT: File too short"); 138 return; 139 } 140 i = sizeof(CDATTableHeader); 141 num_ent = 1; 142 while (i < file_size) { 143 hdr = (CDATSubHeader *)(cdat->buf + i); 144 cdat_len_check(hdr, errp); 145 i += hdr->length; 146 num_ent++; 147 } 148 if (i != file_size) { 149 error_setg(errp, "CDAT: File length mismatch"); 150 return; 151 } 152 153 cdat_st = g_malloc0(sizeof(*cdat_st) * num_ent); 154 if (!cdat_st) { 155 error_setg(errp, "CDAT: Failed to allocate entry array"); 156 return; 157 } 158 159 /* Set CDAT header, Entry = 0 */ 160 cdat_st[0].base = cdat->buf; 161 cdat_st[0].length = sizeof(CDATTableHeader); 162 i = 0; 163 164 while (i < cdat_st[0].length) { 165 sum += cdat->buf[i++]; 166 } 167 168 /* Read CDAT structures */ 169 while (i < file_size) { 170 hdr = (CDATSubHeader *)(cdat->buf + i); 171 cdat_len_check(hdr, errp); 172 173 cdat_st[ent].base = hdr; 174 cdat_st[ent].length = hdr->length; 175 176 while (cdat->buf + i < 177 (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { 178 assert(i < file_size); 179 sum += cdat->buf[i++]; 180 } 181 182 ent++; 183 } 184 185 if (sum != 0) { 186 warn_report("CDAT: Found checksum mismatch in %s", cdat->filename); 187 } 188 cdat->entry_len = num_ent; 189 cdat->entry = g_steal_pointer(&cdat_st); 190 } 191 192 void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) 193 { 194 CDATObject *cdat = &cxl_cstate->cdat; 195 196 if (cdat->filename) { 197 ct3_load_cdat(cdat, errp); 198 } else { 199 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 if (cdat->buf) { 222 free(cdat->buf); 223 } 224 } 225