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