/* * CXL CDAT Structure * * Copyright (C) 2021 Avery Design Systems, Inc. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "hw/pci/pci.h" #include "hw/cxl/cxl.h" #include "qapi/error.h" #include "qemu/error-report.h" static void cdat_len_check(CDATSubHeader *hdr, Error **errp) { assert(hdr->length); assert(hdr->reserved == 0); switch (hdr->type) { case CDAT_TYPE_DSMAS: assert(hdr->length == sizeof(CDATDsmas)); break; case CDAT_TYPE_DSLBIS: assert(hdr->length == sizeof(CDATDslbis)); break; case CDAT_TYPE_DSMSCIS: assert(hdr->length == sizeof(CDATDsmscis)); break; case CDAT_TYPE_DSIS: assert(hdr->length == sizeof(CDATDsis)); break; case CDAT_TYPE_DSEMTS: assert(hdr->length == sizeof(CDATDsemts)); break; case CDAT_TYPE_SSLBIS: assert(hdr->length >= sizeof(CDATSslbisHeader)); assert((hdr->length - sizeof(CDATSslbisHeader)) % sizeof(CDATSslbe) == 0); break; default: error_setg(errp, "Type %d is reserved", hdr->type); } } static void ct3_build_cdat(CDATObject *cdat, Error **errp) { g_autofree CDATTableHeader *cdat_header = NULL; g_autofree CDATEntry *cdat_st = NULL; uint8_t sum = 0; uint8_t *hdr_buf; int ent, i; /* Use default table if fopen == NULL */ assert(cdat->build_cdat_table); cdat_header = g_malloc0(sizeof(*cdat_header)); if (!cdat_header) { error_setg(errp, "Failed to allocate CDAT header"); return; } cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private); if (cdat->built_buf_len <= 0) { /* Build later as not all data available yet */ cdat->to_update = true; return; } cdat->to_update = false; cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1)); if (!cdat_st) { error_setg(errp, "Failed to allocate CDAT entry array"); return; } /* Entry 0 for CDAT header, starts with Entry 1 */ for (ent = 1; ent < cdat->built_buf_len + 1; ent++) { CDATSubHeader *hdr = cdat->built_buf[ent - 1]; uint8_t *buf = (uint8_t *)cdat->built_buf[ent - 1]; cdat_st[ent].base = hdr; cdat_st[ent].length = hdr->length; cdat_header->length += hdr->length; for (i = 0; i < hdr->length; i++) { sum += buf[i]; } } /* CDAT header */ cdat_header->revision = CXL_CDAT_REV; /* For now, no runtime updates */ cdat_header->sequence = 0; cdat_header->length += sizeof(CDATTableHeader); hdr_buf = (uint8_t *)cdat_header; for (i = 0; i < sizeof(*cdat_header); i++) { sum += hdr_buf[i]; } /* Sum of all bytes including checksum must be 0 */ cdat_header->checksum = ~sum + 1; cdat_st[0].base = g_steal_pointer(&cdat_header); cdat_st[0].length = sizeof(*cdat_header); cdat->entry_len = 1 + cdat->built_buf_len; cdat->entry = g_steal_pointer(&cdat_st); } static void ct3_load_cdat(CDATObject *cdat, Error **errp) { g_autofree CDATEntry *cdat_st = NULL; g_autofree uint8_t *buf = NULL; uint8_t sum = 0; int num_ent; int i = 0, ent = 1; gsize file_size = 0; CDATSubHeader *hdr; GError *error = NULL; /* Read CDAT file and create its cache */ if (!g_file_get_contents(cdat->filename, (gchar **)&buf, &file_size, &error)) { error_setg(errp, "CDAT: File read failed: %s", error->message); g_error_free(error); return; } if (file_size < sizeof(CDATTableHeader)) { error_setg(errp, "CDAT: File too short"); return; } i = sizeof(CDATTableHeader); num_ent = 1; while (i < file_size) { hdr = (CDATSubHeader *)(buf + i); if (i + sizeof(CDATSubHeader) > file_size) { error_setg(errp, "CDAT: Truncated table"); return; } cdat_len_check(hdr, errp); i += hdr->length; if (i > file_size) { error_setg(errp, "CDAT: Truncated table"); return; } num_ent++; } if (i != file_size) { error_setg(errp, "CDAT: File length mismatch"); return; } cdat_st = g_new0(CDATEntry, num_ent); /* Set CDAT header, Entry = 0 */ cdat_st[0].base = buf; cdat_st[0].length = sizeof(CDATTableHeader); i = 0; while (i < cdat_st[0].length) { sum += buf[i++]; } /* Read CDAT structures */ while (i < file_size) { hdr = (CDATSubHeader *)(buf + i); cdat_st[ent].base = hdr; cdat_st[ent].length = hdr->length; while (buf + i < (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { assert(i < file_size); sum += buf[i++]; } ent++; } if (sum != 0) { warn_report("CDAT: Found checksum mismatch in %s", cdat->filename); } cdat->entry_len = num_ent; cdat->entry = g_steal_pointer(&cdat_st); cdat->buf = g_steal_pointer(&buf); } void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) { CDATObject *cdat = &cxl_cstate->cdat; if (cdat->filename) { ct3_load_cdat(cdat, errp); } else { ct3_build_cdat(cdat, errp); } } void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp) { CDATObject *cdat = &cxl_cstate->cdat; if (cdat->to_update) { ct3_build_cdat(cdat, errp); } } void cxl_doe_cdat_release(CXLComponentState *cxl_cstate) { CDATObject *cdat = &cxl_cstate->cdat; free(cdat->entry); if (cdat->built_buf) { cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len, cdat->private); } g_free(cdat->buf); }