cxl.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /*
  2. * CXL ACPI Implementation
  3. *
  4. * Copyright(C) 2020 Intel Corporation.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, see <http://www.gnu.org/licenses/>
  18. */
  19. #include "qemu/osdep.h"
  20. #include "hw/sysbus.h"
  21. #include "hw/pci/pci_bridge.h"
  22. #include "hw/pci/pci_host.h"
  23. #include "hw/cxl/cxl.h"
  24. #include "hw/mem/memory-device.h"
  25. #include "hw/acpi/acpi.h"
  26. #include "hw/acpi/aml-build.h"
  27. #include "hw/acpi/bios-linker-loader.h"
  28. #include "hw/acpi/cxl.h"
  29. #include "qapi/error.h"
  30. #include "qemu/uuid.h"
  31. void build_cxl_dsm_method(Aml *dev)
  32. {
  33. Aml *method, *ifctx, *ifctx2;
  34. method = aml_method("_DSM", 4, AML_SERIALIZED);
  35. {
  36. Aml *function, *uuid;
  37. uuid = aml_arg(0);
  38. function = aml_arg(2);
  39. /* CXL spec v3.0 9.17.3.1 _DSM Function for Retrieving QTG ID */
  40. ifctx = aml_if(aml_equal(
  41. uuid, aml_touuid("F365F9A6-A7DE-4071-A66A-B40C0B4F8E52")));
  42. /* Function 0, standard DSM query function */
  43. ifctx2 = aml_if(aml_equal(function, aml_int(0)));
  44. {
  45. uint8_t byte_list[1] = { 0x01 }; /* function 1 only */
  46. aml_append(ifctx2,
  47. aml_return(aml_buffer(sizeof(byte_list), byte_list)));
  48. }
  49. aml_append(ifctx, ifctx2);
  50. /*
  51. * Function 1
  52. * Creating a package with static values. The max supported QTG ID will
  53. * be 1 and recommended QTG IDs are 0 and then 1.
  54. * The values here are statically created to simplify emulation. Values
  55. * from a real BIOS would be determined by the performance of all the
  56. * present CXL memory and then assigned.
  57. */
  58. ifctx2 = aml_if(aml_equal(function, aml_int(1)));
  59. {
  60. Aml *pak, *pak1;
  61. /*
  62. * Return: A package containing two elements - a WORD that returns
  63. * the maximum throttling group that the platform supports, and a
  64. * package containing the QTG ID(s) that the platform recommends.
  65. * Package {
  66. * Max Supported QTG ID
  67. * Package {QTG Recommendations}
  68. * }
  69. *
  70. * While the SPEC specified WORD that hints at the value being
  71. * 16bit, the ACPI dump of BIOS DSDT table showed that the values
  72. * are integers with no specific size specification. aml_int() will
  73. * be used for the values.
  74. */
  75. pak1 = aml_package(2);
  76. /* Set QTG ID of 0 */
  77. aml_append(pak1, aml_int(0));
  78. /* Set QTG ID of 1 */
  79. aml_append(pak1, aml_int(1));
  80. pak = aml_package(2);
  81. /* Set Max QTG 1 */
  82. aml_append(pak, aml_int(1));
  83. aml_append(pak, pak1);
  84. aml_append(ifctx2, aml_return(pak));
  85. }
  86. aml_append(ifctx, ifctx2);
  87. }
  88. aml_append(method, ifctx);
  89. aml_append(dev, method);
  90. }
  91. static void cedt_build_chbs(GArray *table_data, PXBCXLDev *cxl)
  92. {
  93. PXBDev *pxb = PXB_DEV(cxl);
  94. SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl_host_bridge);
  95. struct MemoryRegion *mr = sbd->mmio[0].memory;
  96. /* Type */
  97. build_append_int_noprefix(table_data, 0, 1);
  98. /* Reserved */
  99. build_append_int_noprefix(table_data, 0, 1);
  100. /* Record Length */
  101. build_append_int_noprefix(table_data, 32, 2);
  102. /* UID - currently equal to bus number */
  103. build_append_int_noprefix(table_data, pxb->bus_nr, 4);
  104. /* Version */
  105. build_append_int_noprefix(table_data, 1, 4);
  106. /* Reserved */
  107. build_append_int_noprefix(table_data, 0, 4);
  108. /* Base - subregion within a container that is in PA space */
  109. build_append_int_noprefix(table_data, mr->container->addr + mr->addr, 8);
  110. /* Length */
  111. build_append_int_noprefix(table_data, memory_region_size(mr), 8);
  112. }
  113. /*
  114. * CFMWS entries in CXL 2.0 ECN: CEDT CFMWS & QTG _DSM.
  115. * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory
  116. * interleaving.
  117. */
  118. static void cedt_build_cfmws(GArray *table_data, CXLState *cxls)
  119. {
  120. GList *it;
  121. for (it = cxls->fixed_windows; it; it = it->next) {
  122. CXLFixedWindow *fw = it->data;
  123. int i;
  124. /* Type */
  125. build_append_int_noprefix(table_data, 1, 1);
  126. /* Reserved */
  127. build_append_int_noprefix(table_data, 0, 1);
  128. /* Record Length */
  129. build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2);
  130. /* Reserved */
  131. build_append_int_noprefix(table_data, 0, 4);
  132. /* Base HPA */
  133. build_append_int_noprefix(table_data, fw->mr.addr, 8);
  134. /* Window Size */
  135. build_append_int_noprefix(table_data, fw->size, 8);
  136. /* Host Bridge Interleave Ways */
  137. build_append_int_noprefix(table_data, fw->enc_int_ways, 1);
  138. /* Host Bridge Interleave Arithmetic */
  139. build_append_int_noprefix(table_data, 0, 1);
  140. /* Reserved */
  141. build_append_int_noprefix(table_data, 0, 2);
  142. /* Host Bridge Interleave Granularity */
  143. build_append_int_noprefix(table_data, fw->enc_int_gran, 4);
  144. /* Window Restrictions */
  145. build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */
  146. /* QTG ID */
  147. build_append_int_noprefix(table_data, 0, 2);
  148. /* Host Bridge List (list of UIDs - currently bus_nr) */
  149. for (i = 0; i < fw->num_targets; i++) {
  150. g_assert(fw->target_hbs[i]);
  151. build_append_int_noprefix(table_data, PXB_DEV(fw->target_hbs[i])->bus_nr, 4);
  152. }
  153. }
  154. }
  155. static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
  156. {
  157. Aml *cedt = opaque;
  158. if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEV)) {
  159. cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj));
  160. }
  161. return 0;
  162. }
  163. void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
  164. BIOSLinker *linker, const char *oem_id,
  165. const char *oem_table_id, CXLState *cxl_state)
  166. {
  167. Aml *cedt;
  168. AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
  169. .oem_table_id = oem_table_id };
  170. acpi_add_table(table_offsets, table_data);
  171. acpi_table_begin(&table, table_data);
  172. cedt = init_aml_allocator();
  173. /* reserve space for CEDT header */
  174. object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
  175. cedt_build_cfmws(cedt->buf, cxl_state);
  176. /* copy AML table into ACPI tables blob and patch header there */
  177. g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
  178. free_aml_allocator();
  179. acpi_table_end(linker, &table);
  180. }
  181. static Aml *__build_cxl_osc_method(void)
  182. {
  183. Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
  184. Aml *a_ctrl = aml_local(0);
  185. Aml *a_cdw1 = aml_name("CDW1");
  186. method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
  187. /* CDW1 is used for the return value so is present whether or not a match occurs */
  188. aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
  189. /*
  190. * Generate shared section between:
  191. * CXL 2.0 - 9.14.2.1.4 and
  192. * PCI Firmware Specification 3.0
  193. * 4.5.1. _OSC Interface for PCI Host Bridge Devices
  194. * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
  195. * identified by the Universal Unique IDentifier (UUID)
  196. * 33DB4D5B-1FF7-401C-9657-7441C03DD766
  197. * The _OSC interface for a CXL Host bridge is
  198. * identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC
  199. * A CXL Host bridge is compatible with a PCI host bridge so
  200. * for the shared section match both.
  201. */
  202. if_uuid = aml_if(
  203. aml_lor(aml_equal(aml_arg(0),
  204. aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")),
  205. aml_equal(aml_arg(0),
  206. aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))));
  207. aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
  208. aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
  209. aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl));
  210. /*
  211. *
  212. * Allows OS control for all 5 features:
  213. * PCIeHotplug SHPCHotplug PME AER PCIeCapability
  214. */
  215. aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
  216. /*
  217. * Check _OSC revision.
  218. * PCI Firmware specification 3.3 and CXL 2.0 both use revision 1
  219. * Unknown Revision is CDW1 - BIT (3)
  220. */
  221. if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
  222. aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1));
  223. aml_append(if_uuid, if_arg1_not_1);
  224. if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
  225. /* Capability bits were masked */
  226. aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
  227. aml_append(if_uuid, if_caps_masked);
  228. aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP")));
  229. aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL")));
  230. /* Update DWORD3 (the return value) */
  231. aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3")));
  232. /* CXL only section as per CXL 2.0 - 9.14.2.1.4 */
  233. if_cxl = aml_if(aml_equal(
  234. aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")));
  235. /* CXL support field */
  236. aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
  237. /* CXL capabilities */
  238. aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
  239. aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
  240. aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
  241. /* CXL 2.0 Port/Device Register access */
  242. aml_append(if_cxl,
  243. aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
  244. aml_append(if_uuid, if_cxl);
  245. aml_append(if_uuid, aml_return(aml_arg(3)));
  246. aml_append(method, if_uuid);
  247. /*
  248. * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
  249. * ACPI 6.4 - 6.2.11
  250. * Unrecognised UUID - BIT(2)
  251. */
  252. else_uuid = aml_else();
  253. aml_append(else_uuid,
  254. aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1")));
  255. aml_append(else_uuid, aml_return(aml_arg(3)));
  256. aml_append(method, else_uuid);
  257. return method;
  258. }
  259. void build_cxl_osc_method(Aml *dev)
  260. {
  261. aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
  262. aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
  263. aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
  264. aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
  265. aml_append(dev, __build_cxl_osc_method());
  266. }