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