1aba578bdSHuai-Cheng Kuo /* 2aba578bdSHuai-Cheng Kuo * CXL CDAT Structure 3aba578bdSHuai-Cheng Kuo * 4aba578bdSHuai-Cheng Kuo * Copyright (C) 2021 Avery Design Systems, Inc. 5aba578bdSHuai-Cheng Kuo * 6aba578bdSHuai-Cheng Kuo * This work is licensed under the terms of the GNU GPL, version 2 or later. 7aba578bdSHuai-Cheng Kuo * See the COPYING file in the top-level directory. 8aba578bdSHuai-Cheng Kuo */ 9aba578bdSHuai-Cheng Kuo 10aba578bdSHuai-Cheng Kuo #include "qemu/osdep.h" 11aba578bdSHuai-Cheng Kuo #include "hw/pci/pci.h" 12aba578bdSHuai-Cheng Kuo #include "hw/cxl/cxl.h" 13aba578bdSHuai-Cheng Kuo #include "qapi/error.h" 14aba578bdSHuai-Cheng Kuo #include "qemu/error-report.h" 15aba578bdSHuai-Cheng Kuo 16aba578bdSHuai-Cheng Kuo static void cdat_len_check(CDATSubHeader *hdr, Error **errp) 17aba578bdSHuai-Cheng Kuo { 18aba578bdSHuai-Cheng Kuo assert(hdr->length); 19aba578bdSHuai-Cheng Kuo assert(hdr->reserved == 0); 20aba578bdSHuai-Cheng Kuo 21aba578bdSHuai-Cheng Kuo switch (hdr->type) { 22aba578bdSHuai-Cheng Kuo case CDAT_TYPE_DSMAS: 23aba578bdSHuai-Cheng Kuo assert(hdr->length == sizeof(CDATDsmas)); 24aba578bdSHuai-Cheng Kuo break; 25aba578bdSHuai-Cheng Kuo case CDAT_TYPE_DSLBIS: 26aba578bdSHuai-Cheng Kuo assert(hdr->length == sizeof(CDATDslbis)); 27aba578bdSHuai-Cheng Kuo break; 28aba578bdSHuai-Cheng Kuo case CDAT_TYPE_DSMSCIS: 29aba578bdSHuai-Cheng Kuo assert(hdr->length == sizeof(CDATDsmscis)); 30aba578bdSHuai-Cheng Kuo break; 31aba578bdSHuai-Cheng Kuo case CDAT_TYPE_DSIS: 32aba578bdSHuai-Cheng Kuo assert(hdr->length == sizeof(CDATDsis)); 33aba578bdSHuai-Cheng Kuo break; 34aba578bdSHuai-Cheng Kuo case CDAT_TYPE_DSEMTS: 35aba578bdSHuai-Cheng Kuo assert(hdr->length == sizeof(CDATDsemts)); 36aba578bdSHuai-Cheng Kuo break; 37aba578bdSHuai-Cheng Kuo case CDAT_TYPE_SSLBIS: 38aba578bdSHuai-Cheng Kuo assert(hdr->length >= sizeof(CDATSslbisHeader)); 39aba578bdSHuai-Cheng Kuo assert((hdr->length - sizeof(CDATSslbisHeader)) % 40aba578bdSHuai-Cheng Kuo sizeof(CDATSslbe) == 0); 41aba578bdSHuai-Cheng Kuo break; 42aba578bdSHuai-Cheng Kuo default: 43aba578bdSHuai-Cheng Kuo error_setg(errp, "Type %d is reserved", hdr->type); 44aba578bdSHuai-Cheng Kuo } 45aba578bdSHuai-Cheng Kuo } 46aba578bdSHuai-Cheng Kuo 47a133d207SZhao Liu static bool ct3_build_cdat(CDATObject *cdat, Error **errp) 48aba578bdSHuai-Cheng Kuo { 49aba578bdSHuai-Cheng Kuo g_autofree CDATTableHeader *cdat_header = NULL; 50aba578bdSHuai-Cheng Kuo g_autofree CDATEntry *cdat_st = NULL; 51aba578bdSHuai-Cheng Kuo uint8_t sum = 0; 5264fdad5eSIra Weiny uint8_t *hdr_buf; 53aba578bdSHuai-Cheng Kuo int ent, i; 54aba578bdSHuai-Cheng Kuo 55aba578bdSHuai-Cheng Kuo /* Use default table if fopen == NULL */ 56aba578bdSHuai-Cheng Kuo assert(cdat->build_cdat_table); 57aba578bdSHuai-Cheng Kuo 58aba578bdSHuai-Cheng Kuo cdat_header = g_malloc0(sizeof(*cdat_header)); 59aba578bdSHuai-Cheng Kuo if (!cdat_header) { 60aba578bdSHuai-Cheng Kuo error_setg(errp, "Failed to allocate CDAT header"); 61a133d207SZhao Liu return false; 62aba578bdSHuai-Cheng Kuo } 63aba578bdSHuai-Cheng Kuo 64b342489aSJonathan Cameron cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, 65b342489aSJonathan Cameron cdat->private); 66aba578bdSHuai-Cheng Kuo 67c62926f7SIra Weiny if (cdat->built_buf_len <= 0) { 68aba578bdSHuai-Cheng Kuo /* Build later as not all data available yet */ 69aba578bdSHuai-Cheng Kuo cdat->to_update = true; 70a133d207SZhao Liu return true; 71aba578bdSHuai-Cheng Kuo } 72aba578bdSHuai-Cheng Kuo cdat->to_update = false; 73aba578bdSHuai-Cheng Kuo 74aba578bdSHuai-Cheng Kuo cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1)); 75aba578bdSHuai-Cheng Kuo if (!cdat_st) { 76aba578bdSHuai-Cheng Kuo error_setg(errp, "Failed to allocate CDAT entry array"); 77a133d207SZhao Liu return false; 78aba578bdSHuai-Cheng Kuo } 79aba578bdSHuai-Cheng Kuo 80aba578bdSHuai-Cheng Kuo /* Entry 0 for CDAT header, starts with Entry 1 */ 81aba578bdSHuai-Cheng Kuo for (ent = 1; ent < cdat->built_buf_len + 1; ent++) { 82aba578bdSHuai-Cheng Kuo CDATSubHeader *hdr = cdat->built_buf[ent - 1]; 83aba578bdSHuai-Cheng Kuo uint8_t *buf = (uint8_t *)cdat->built_buf[ent - 1]; 84aba578bdSHuai-Cheng Kuo 85aba578bdSHuai-Cheng Kuo cdat_st[ent].base = hdr; 86aba578bdSHuai-Cheng Kuo cdat_st[ent].length = hdr->length; 87aba578bdSHuai-Cheng Kuo 88aba578bdSHuai-Cheng Kuo cdat_header->length += hdr->length; 89aba578bdSHuai-Cheng Kuo for (i = 0; i < hdr->length; i++) { 90aba578bdSHuai-Cheng Kuo sum += buf[i]; 91aba578bdSHuai-Cheng Kuo } 92aba578bdSHuai-Cheng Kuo } 93aba578bdSHuai-Cheng Kuo 94aba578bdSHuai-Cheng Kuo /* CDAT header */ 95aba578bdSHuai-Cheng Kuo cdat_header->revision = CXL_CDAT_REV; 96aba578bdSHuai-Cheng Kuo /* For now, no runtime updates */ 97aba578bdSHuai-Cheng Kuo cdat_header->sequence = 0; 98aba578bdSHuai-Cheng Kuo cdat_header->length += sizeof(CDATTableHeader); 9964fdad5eSIra Weiny 10064fdad5eSIra Weiny hdr_buf = (uint8_t *)cdat_header; 10164fdad5eSIra Weiny for (i = 0; i < sizeof(*cdat_header); i++) { 10264fdad5eSIra Weiny sum += hdr_buf[i]; 10364fdad5eSIra Weiny } 10464fdad5eSIra Weiny 105aba578bdSHuai-Cheng Kuo /* Sum of all bytes including checksum must be 0 */ 106aba578bdSHuai-Cheng Kuo cdat_header->checksum = ~sum + 1; 107aba578bdSHuai-Cheng Kuo 108aba578bdSHuai-Cheng Kuo cdat_st[0].base = g_steal_pointer(&cdat_header); 109aba578bdSHuai-Cheng Kuo cdat_st[0].length = sizeof(*cdat_header); 110aba578bdSHuai-Cheng Kuo cdat->entry_len = 1 + cdat->built_buf_len; 111aba578bdSHuai-Cheng Kuo cdat->entry = g_steal_pointer(&cdat_st); 112a133d207SZhao Liu return true; 113aba578bdSHuai-Cheng Kuo } 114aba578bdSHuai-Cheng Kuo 1152c5b2d91SZhao Liu static bool ct3_load_cdat(CDATObject *cdat, Error **errp) 116aba578bdSHuai-Cheng Kuo { 117aba578bdSHuai-Cheng Kuo g_autofree CDATEntry *cdat_st = NULL; 118c4e898d5SThomas Huth g_autofree uint8_t *buf = NULL; 119aba578bdSHuai-Cheng Kuo uint8_t sum = 0; 120aba578bdSHuai-Cheng Kuo int num_ent; 12171ba92f3SHao Zeng int i = 0, ent = 1; 12271ba92f3SHao Zeng gsize file_size = 0; 123aba578bdSHuai-Cheng Kuo CDATSubHeader *hdr; 12471ba92f3SHao Zeng GError *error = NULL; 125aba578bdSHuai-Cheng Kuo 126aba578bdSHuai-Cheng Kuo /* Read CDAT file and create its cache */ 1277b22a321SJonathan Cameron if (!g_file_get_contents(cdat->filename, (gchar **)&buf, 12871ba92f3SHao Zeng &file_size, &error)) { 12971ba92f3SHao Zeng error_setg(errp, "CDAT: File read failed: %s", error->message); 13071ba92f3SHao Zeng g_error_free(error); 1312c5b2d91SZhao Liu return false; 132aba578bdSHuai-Cheng Kuo } 133aba578bdSHuai-Cheng Kuo if (file_size < sizeof(CDATTableHeader)) { 134aba578bdSHuai-Cheng Kuo error_setg(errp, "CDAT: File too short"); 1352c5b2d91SZhao Liu return false; 136aba578bdSHuai-Cheng Kuo } 137aba578bdSHuai-Cheng Kuo i = sizeof(CDATTableHeader); 138aba578bdSHuai-Cheng Kuo num_ent = 1; 139aba578bdSHuai-Cheng Kuo while (i < file_size) { 1407b22a321SJonathan Cameron hdr = (CDATSubHeader *)(buf + i); 1417b22a321SJonathan Cameron if (i + sizeof(CDATSubHeader) > file_size) { 1427b22a321SJonathan Cameron error_setg(errp, "CDAT: Truncated table"); 1432c5b2d91SZhao Liu return false; 1447b22a321SJonathan Cameron } 145aba578bdSHuai-Cheng Kuo cdat_len_check(hdr, errp); 146aba578bdSHuai-Cheng Kuo i += hdr->length; 1477b22a321SJonathan Cameron if (i > file_size) { 1487b22a321SJonathan Cameron error_setg(errp, "CDAT: Truncated table"); 1492c5b2d91SZhao Liu return false; 1507b22a321SJonathan Cameron } 151aba578bdSHuai-Cheng Kuo num_ent++; 152aba578bdSHuai-Cheng Kuo } 153aba578bdSHuai-Cheng Kuo if (i != file_size) { 154f0376c3fSMichael Tokarev error_setg(errp, "CDAT: File length mismatch"); 1552c5b2d91SZhao Liu return false; 156aba578bdSHuai-Cheng Kuo } 157aba578bdSHuai-Cheng Kuo 1587b22a321SJonathan Cameron cdat_st = g_new0(CDATEntry, num_ent); 159aba578bdSHuai-Cheng Kuo 160aba578bdSHuai-Cheng Kuo /* Set CDAT header, Entry = 0 */ 1617b22a321SJonathan Cameron cdat_st[0].base = buf; 162aba578bdSHuai-Cheng Kuo cdat_st[0].length = sizeof(CDATTableHeader); 163aba578bdSHuai-Cheng Kuo i = 0; 164aba578bdSHuai-Cheng Kuo 165aba578bdSHuai-Cheng Kuo while (i < cdat_st[0].length) { 1667b22a321SJonathan Cameron sum += buf[i++]; 167aba578bdSHuai-Cheng Kuo } 168aba578bdSHuai-Cheng Kuo 169aba578bdSHuai-Cheng Kuo /* Read CDAT structures */ 170aba578bdSHuai-Cheng Kuo while (i < file_size) { 1717b22a321SJonathan Cameron hdr = (CDATSubHeader *)(buf + i); 172aba578bdSHuai-Cheng Kuo cdat_st[ent].base = hdr; 173aba578bdSHuai-Cheng Kuo cdat_st[ent].length = hdr->length; 174aba578bdSHuai-Cheng Kuo 175c4e898d5SThomas Huth while (buf + i < (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { 176aba578bdSHuai-Cheng Kuo assert(i < file_size); 1777b22a321SJonathan Cameron sum += buf[i++]; 178aba578bdSHuai-Cheng Kuo } 179aba578bdSHuai-Cheng Kuo 180aba578bdSHuai-Cheng Kuo ent++; 181aba578bdSHuai-Cheng Kuo } 182aba578bdSHuai-Cheng Kuo 183aba578bdSHuai-Cheng Kuo if (sum != 0) { 184aba578bdSHuai-Cheng Kuo warn_report("CDAT: Found checksum mismatch in %s", cdat->filename); 185aba578bdSHuai-Cheng Kuo } 186aba578bdSHuai-Cheng Kuo cdat->entry_len = num_ent; 187aba578bdSHuai-Cheng Kuo cdat->entry = g_steal_pointer(&cdat_st); 1887b22a321SJonathan Cameron cdat->buf = g_steal_pointer(&buf); 1892c5b2d91SZhao Liu return true; 190aba578bdSHuai-Cheng Kuo } 191aba578bdSHuai-Cheng Kuo 192*e0ddabc6SZhao Liu bool cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) 193aba578bdSHuai-Cheng Kuo { 194aba578bdSHuai-Cheng Kuo CDATObject *cdat = &cxl_cstate->cdat; 195aba578bdSHuai-Cheng Kuo 196aba578bdSHuai-Cheng Kuo if (cdat->filename) { 197*e0ddabc6SZhao Liu return ct3_load_cdat(cdat, errp); 198aba578bdSHuai-Cheng Kuo } else { 199*e0ddabc6SZhao Liu return ct3_build_cdat(cdat, errp); 200aba578bdSHuai-Cheng Kuo } 201aba578bdSHuai-Cheng Kuo } 202aba578bdSHuai-Cheng Kuo 203aba578bdSHuai-Cheng Kuo void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp) 204aba578bdSHuai-Cheng Kuo { 205aba578bdSHuai-Cheng Kuo CDATObject *cdat = &cxl_cstate->cdat; 206aba578bdSHuai-Cheng Kuo 207aba578bdSHuai-Cheng Kuo if (cdat->to_update) { 208aba578bdSHuai-Cheng Kuo ct3_build_cdat(cdat, errp); 209aba578bdSHuai-Cheng Kuo } 210aba578bdSHuai-Cheng Kuo } 211aba578bdSHuai-Cheng Kuo 212aba578bdSHuai-Cheng Kuo void cxl_doe_cdat_release(CXLComponentState *cxl_cstate) 213aba578bdSHuai-Cheng Kuo { 214aba578bdSHuai-Cheng Kuo CDATObject *cdat = &cxl_cstate->cdat; 215aba578bdSHuai-Cheng Kuo 216aba578bdSHuai-Cheng Kuo free(cdat->entry); 217aba578bdSHuai-Cheng Kuo if (cdat->built_buf) { 218aba578bdSHuai-Cheng Kuo cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len, 219aba578bdSHuai-Cheng Kuo cdat->private); 220aba578bdSHuai-Cheng Kuo } 22171ba92f3SHao Zeng g_free(cdat->buf); 222aba578bdSHuai-Cheng Kuo } 223