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 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; 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; 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; 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 } 113 114 static void ct3_load_cdat(CDATObject *cdat, Error **errp) 115 { 116 g_autofree CDATEntry *cdat_st = NULL; 117 g_autofree uint8_t *buf = NULL; 118 uint8_t sum = 0; 119 int num_ent; 120 int i = 0, ent = 1; 121 gsize file_size = 0; 122 CDATSubHeader *hdr; 123 GError *error = NULL; 124 125 /* Read CDAT file and create its cache */ 126 if (!g_file_get_contents(cdat->filename, (gchar **)&buf, 127 &file_size, &error)) { 128 error_setg(errp, "CDAT: File read failed: %s", error->message); 129 g_error_free(error); 130 return; 131 } 132 if (file_size < sizeof(CDATTableHeader)) { 133 error_setg(errp, "CDAT: File too short"); 134 return; 135 } 136 i = sizeof(CDATTableHeader); 137 num_ent = 1; 138 while (i < file_size) { 139 hdr = (CDATSubHeader *)(buf + i); 140 if (i + sizeof(CDATSubHeader) > file_size) { 141 error_setg(errp, "CDAT: Truncated table"); 142 return; 143 } 144 cdat_len_check(hdr, errp); 145 i += hdr->length; 146 if (i > file_size) { 147 error_setg(errp, "CDAT: Truncated table"); 148 return; 149 } 150 num_ent++; 151 } 152 if (i != file_size) { 153 error_setg(errp, "CDAT: File length mismatch"); 154 return; 155 } 156 157 cdat_st = g_new0(CDATEntry, num_ent); 158 159 /* Set CDAT header, Entry = 0 */ 160 cdat_st[0].base = buf; 161 cdat_st[0].length = sizeof(CDATTableHeader); 162 i = 0; 163 164 while (i < cdat_st[0].length) { 165 sum += buf[i++]; 166 } 167 168 /* Read CDAT structures */ 169 while (i < file_size) { 170 hdr = (CDATSubHeader *)(buf + i); 171 cdat_st[ent].base = hdr; 172 cdat_st[ent].length = hdr->length; 173 174 while (buf + i < (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { 175 assert(i < file_size); 176 sum += buf[i++]; 177 } 178 179 ent++; 180 } 181 182 if (sum != 0) { 183 warn_report("CDAT: Found checksum mismatch in %s", cdat->filename); 184 } 185 cdat->entry_len = num_ent; 186 cdat->entry = g_steal_pointer(&cdat_st); 187 cdat->buf = g_steal_pointer(&buf); 188 } 189 190 void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) 191 { 192 CDATObject *cdat = &cxl_cstate->cdat; 193 194 if (cdat->filename) { 195 ct3_load_cdat(cdat, errp); 196 } else { 197 ct3_build_cdat(cdat, errp); 198 } 199 } 200 201 void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp) 202 { 203 CDATObject *cdat = &cxl_cstate->cdat; 204 205 if (cdat->to_update) { 206 ct3_build_cdat(cdat, errp); 207 } 208 } 209 210 void cxl_doe_cdat_release(CXLComponentState *cxl_cstate) 211 { 212 CDATObject *cdat = &cxl_cstate->cdat; 213 214 free(cdat->entry); 215 if (cdat->built_buf) { 216 cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len, 217 cdat->private); 218 } 219 g_free(cdat->buf); 220 } 221