core.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * AMD Address Translation Library
  4. *
  5. * core.c : Module init and base translation functions
  6. *
  7. * Copyright (c) 2023, Advanced Micro Devices, Inc.
  8. * All Rights Reserved.
  9. *
  10. * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
  11. */
  12. #include <linux/module.h>
  13. #include <asm/cpu_device_id.h>
  14. #include "internal.h"
  15. struct df_config df_cfg __read_mostly;
  16. static int addr_over_limit(struct addr_ctx *ctx)
  17. {
  18. u64 dram_limit_addr;
  19. if (df_cfg.rev >= DF4)
  20. dram_limit_addr = FIELD_GET(DF4_DRAM_LIMIT_ADDR, ctx->map.limit);
  21. else
  22. dram_limit_addr = FIELD_GET(DF2_DRAM_LIMIT_ADDR, ctx->map.limit);
  23. dram_limit_addr <<= DF_DRAM_BASE_LIMIT_LSB;
  24. dram_limit_addr |= GENMASK(DF_DRAM_BASE_LIMIT_LSB - 1, 0);
  25. /* Is calculated system address above DRAM limit address? */
  26. if (ctx->ret_addr > dram_limit_addr) {
  27. atl_debug(ctx, "Calculated address (0x%016llx) > DRAM limit (0x%016llx)",
  28. ctx->ret_addr, dram_limit_addr);
  29. return -EINVAL;
  30. }
  31. return 0;
  32. }
  33. static bool legacy_hole_en(struct addr_ctx *ctx)
  34. {
  35. u32 reg = ctx->map.base;
  36. if (df_cfg.rev >= DF4)
  37. reg = ctx->map.ctl;
  38. return FIELD_GET(DF_LEGACY_MMIO_HOLE_EN, reg);
  39. }
  40. static u64 add_legacy_hole(struct addr_ctx *ctx, u64 addr)
  41. {
  42. if (!legacy_hole_en(ctx))
  43. return addr;
  44. if (addr >= df_cfg.dram_hole_base)
  45. addr += (BIT_ULL(32) - df_cfg.dram_hole_base);
  46. return addr;
  47. }
  48. static u64 remove_legacy_hole(struct addr_ctx *ctx, u64 addr)
  49. {
  50. if (!legacy_hole_en(ctx))
  51. return addr;
  52. if (addr >= df_cfg.dram_hole_base)
  53. addr -= (BIT_ULL(32) - df_cfg.dram_hole_base);
  54. return addr;
  55. }
  56. static u64 get_base_addr(struct addr_ctx *ctx)
  57. {
  58. u64 base_addr;
  59. if (df_cfg.rev >= DF4)
  60. base_addr = FIELD_GET(DF4_BASE_ADDR, ctx->map.base);
  61. else
  62. base_addr = FIELD_GET(DF2_BASE_ADDR, ctx->map.base);
  63. return base_addr << DF_DRAM_BASE_LIMIT_LSB;
  64. }
  65. u64 add_base_and_hole(struct addr_ctx *ctx, u64 addr)
  66. {
  67. return add_legacy_hole(ctx, addr + get_base_addr(ctx));
  68. }
  69. u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr)
  70. {
  71. return remove_legacy_hole(ctx, addr) - get_base_addr(ctx);
  72. }
  73. static bool late_hole_remove(struct addr_ctx *ctx)
  74. {
  75. if (df_cfg.rev == DF3p5)
  76. return true;
  77. if (df_cfg.rev == DF4)
  78. return true;
  79. if (ctx->map.intlv_mode == DF3_6CHAN)
  80. return true;
  81. return false;
  82. }
  83. unsigned long norm_to_sys_addr(u8 socket_id, u8 die_id, u8 coh_st_inst_id, unsigned long addr)
  84. {
  85. struct addr_ctx ctx;
  86. if (df_cfg.rev == UNKNOWN)
  87. return -EINVAL;
  88. memset(&ctx, 0, sizeof(ctx));
  89. /* Start from the normalized address */
  90. ctx.ret_addr = addr;
  91. ctx.inst_id = coh_st_inst_id;
  92. ctx.inputs.norm_addr = addr;
  93. ctx.inputs.socket_id = socket_id;
  94. ctx.inputs.die_id = die_id;
  95. ctx.inputs.coh_st_inst_id = coh_st_inst_id;
  96. if (legacy_hole_en(&ctx) && !df_cfg.dram_hole_base)
  97. return -EINVAL;
  98. if (determine_node_id(&ctx, socket_id, die_id))
  99. return -EINVAL;
  100. if (get_address_map(&ctx))
  101. return -EINVAL;
  102. if (denormalize_address(&ctx))
  103. return -EINVAL;
  104. if (!late_hole_remove(&ctx))
  105. ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);
  106. if (dehash_address(&ctx))
  107. return -EINVAL;
  108. if (late_hole_remove(&ctx))
  109. ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);
  110. if (addr_over_limit(&ctx))
  111. return -EINVAL;
  112. return ctx.ret_addr;
  113. }
  114. static void check_for_legacy_df_access(void)
  115. {
  116. /*
  117. * All Zen-based systems before Family 19h use the legacy
  118. * DF Indirect Access (FICAA/FICAD) offsets.
  119. */
  120. if (boot_cpu_data.x86 < 0x19) {
  121. df_cfg.flags.legacy_ficaa = true;
  122. return;
  123. }
  124. /* All systems after Family 19h use the current offsets. */
  125. if (boot_cpu_data.x86 > 0x19)
  126. return;
  127. /* Some Family 19h systems use the legacy offsets. */
  128. switch (boot_cpu_data.x86_model) {
  129. case 0x00 ... 0x0f:
  130. case 0x20 ... 0x5f:
  131. df_cfg.flags.legacy_ficaa = true;
  132. }
  133. }
  134. /*
  135. * This library provides functionality for AMD-based systems with a Data Fabric.
  136. * The set of systems with a Data Fabric is equivalent to the set of Zen-based systems
  137. * and the set of systems with the Scalable MCA feature at this time. However, these
  138. * are technically independent things.
  139. *
  140. * It's possible to match on the PCI IDs of the Data Fabric devices, but this will be
  141. * an ever expanding list. Instead, match on the SMCA and Zen features to cover all
  142. * relevant systems.
  143. */
  144. static const struct x86_cpu_id amd_atl_cpuids[] = {
  145. X86_MATCH_FEATURE(X86_FEATURE_SMCA, NULL),
  146. X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
  147. { }
  148. };
  149. MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
  150. static int __init amd_atl_init(void)
  151. {
  152. int ret;
  153. if (!x86_match_cpu(amd_atl_cpuids))
  154. return -ENODEV;
  155. if (!amd_nb_num())
  156. return -ENODEV;
  157. check_for_legacy_df_access();
  158. ret = get_df_system_info();
  159. if (ret)
  160. return ret;
  161. /* Increment this module's recount so that it can't be easily unloaded. */
  162. __module_get(THIS_MODULE);
  163. amd_atl_register_decoder(convert_umc_mca_addr_to_sys_addr);
  164. pr_info("AMD Address Translation Library initialized\n");
  165. return 0;
  166. }
  167. /*
  168. * Exit function is only needed for testing and debug. Module unload must be
  169. * forced to override refcount check.
  170. */
  171. static void __exit amd_atl_exit(void)
  172. {
  173. amd_atl_unregister_decoder();
  174. }
  175. module_init(amd_atl_init);
  176. module_exit(amd_atl_exit);
  177. MODULE_DESCRIPTION("AMD Address Translation Library");
  178. MODULE_LICENSE("GPL");