gh_ctrl.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  5. */
  6. #define pr_fmt(fmt) "gunyah: " fmt
  7. #include <linux/arm-smccc.h>
  8. #include <linux/debugfs.h>
  9. #include <linux/module.h>
  10. #include <linux/kobject.h>
  11. #include <linux/of.h>
  12. #include <linux/printk.h>
  13. #include <linux/slab.h>
  14. #include <linux/gunyah.h>
  15. #include "hcall_ctrl.h"
  16. #define QC_HYP_SMCCC_CALL_UID \
  17. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
  18. ARM_SMCCC_OWNER_VENDOR_HYP, 0x3f01)
  19. #define QC_HYP_SMCCC_REVISION \
  20. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
  21. ARM_SMCCC_OWNER_VENDOR_HYP, 0xff03)
  22. #define QC_HYP_UID0 0x19bd54bd
  23. #define QC_HYP_UID1 0x0b37571b
  24. #define QC_HYP_UID2 0x946f609b
  25. #define QC_HYP_UID3 0x54539de6
  26. #define QC_HYP1_UID0 0xbd54bd19
  27. #define QC_HYP1_UID1 0x1b57370b
  28. #define QC_HYP1_UID2 0x9b606f94
  29. #define QC_HYP1_UID3 0xe69d5354
  30. /* Use */
  31. #undef GH_API_INFO_API_VERSION
  32. #undef GH_API_INFO_BIG_ENDIAN
  33. #undef GH_API_INFO_IS_64BIT
  34. #undef GH_API_INFO_VARIANT
  35. #define GH_API_INFO_API_VERSION(x) (((x) >> 0) & 0x3fff)
  36. #define GH_API_INFO_BIG_ENDIAN(x) (((x) >> 14) & 1)
  37. #define GH_API_INFO_IS_64BIT(x) (((x) >> 15) & 1)
  38. #define GH_API_INFO_VARIANT(x) (((x) >> 56) & 0xff)
  39. #define GH_IDENTIFY_PARTITION_CSPACE(x) (((x) >> 0) & 1)
  40. #define GH_IDENTIFY_DOORBELL(x) (((x) >> 1) & 1)
  41. #define GH_IDENTIFY_MSGQUEUE(x) (((x) >> 2) & 1)
  42. #define GH_IDENTIFY_VIC(x) (((x) >> 3) & 1)
  43. #define GH_IDENTIFY_VPM(x) (((x) >> 4) & 1)
  44. #define GH_IDENTIFY_VCPU(x) (((x) >> 5) & 1)
  45. #define GH_IDENTIFY_MEMEXTENT(x) (((x) >> 6) & 1)
  46. #define GH_IDENTIFY_TRACE_CTRL(x) (((x) >> 7) & 1)
  47. #define GH_IDENTIFY_ROOTVM_CHANNEL(x) (((x) >> 16) & 1)
  48. #define GH_IDENTIFY_SCHEDULER(x) (((x) >> 28) & 0xf)
  49. static bool qc_hyp_calls;
  50. static struct gh_hcall_hyp_identify_resp gunyah_api;
  51. static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
  52. char *buffer)
  53. {
  54. return scnprintf(buffer, PAGE_SIZE, "gunyah\n");
  55. }
  56. static struct kobj_attribute type_attr = __ATTR_RO(type);
  57. static ssize_t api_show(struct kobject *kobj, struct kobj_attribute *attr,
  58. char *buffer)
  59. {
  60. return scnprintf(buffer, PAGE_SIZE, "%d\n",
  61. (int)GH_API_INFO_API_VERSION(gunyah_api.api_info));
  62. }
  63. static struct kobj_attribute api_attr = __ATTR_RO(api);
  64. static ssize_t variant_show(struct kobject *kobj, struct kobj_attribute *attr,
  65. char *buffer)
  66. {
  67. return scnprintf(buffer, PAGE_SIZE, "%d\n",
  68. (int)GH_API_INFO_VARIANT(gunyah_api.api_info));
  69. }
  70. static struct kobj_attribute variant_attr = __ATTR_RO(variant);
  71. static struct attribute *version_attrs[] = { &api_attr.attr,
  72. &variant_attr.attr, NULL };
  73. static const struct attribute_group version_group = {
  74. .name = "version",
  75. .attrs = version_attrs,
  76. };
  77. static int __init gh_sysfs_register(void)
  78. {
  79. int ret;
  80. ret = sysfs_create_file(hypervisor_kobj, &type_attr.attr);
  81. if (ret)
  82. return ret;
  83. return sysfs_create_group(hypervisor_kobj, &version_group);
  84. }
  85. static void __exit gh_sysfs_unregister(void)
  86. {
  87. sysfs_remove_file(hypervisor_kobj, &type_attr.attr);
  88. sysfs_remove_group(hypervisor_kobj, &version_group);
  89. }
  90. #if defined(CONFIG_DEBUG_FS)
  91. #define QC_HYP_SMCCC_UART_DISABLE \
  92. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
  93. ARM_SMCCC_OWNER_VENDOR_HYP, 0x0)
  94. #define QC_HYP_SMCCC_UART_ENABLE \
  95. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
  96. ARM_SMCCC_OWNER_VENDOR_HYP, 0xc)
  97. #define ENABLE 1
  98. #define DISABLE 0
  99. static struct dentry *gh_dbgfs_dir;
  100. static int hyp_uart_enable;
  101. static void gh_control_hyp_uart(int val)
  102. {
  103. switch (val) {
  104. case ENABLE:
  105. if (!hyp_uart_enable) {
  106. hyp_uart_enable = val;
  107. pr_info("Gunyah: enabling HYP UART\n");
  108. arm_smccc_1_1_smc(QC_HYP_SMCCC_UART_ENABLE, NULL);
  109. } else {
  110. pr_info("Gunyah: HYP UART already enabled\n");
  111. }
  112. break;
  113. case DISABLE:
  114. if (hyp_uart_enable) {
  115. hyp_uart_enable = val;
  116. pr_info("Gunyah: disabling HYP UART\n");
  117. arm_smccc_1_1_smc(QC_HYP_SMCCC_UART_DISABLE, NULL);
  118. } else {
  119. pr_info("Gunyah: HYP UART already disabled\n");
  120. }
  121. break;
  122. default:
  123. pr_info("Gunyah: supported values disable(0)/enable(1)\n");
  124. }
  125. }
  126. static int gh_dbgfs_trace_class_set(void *data, u64 val)
  127. {
  128. return gh_error_remap(gh_hcall_trace_update_class_flags(val, 0, NULL));
  129. }
  130. static int gh_dbgfs_trace_class_clear(void *data, u64 val)
  131. {
  132. return gh_error_remap(gh_hcall_trace_update_class_flags(0, val, NULL));
  133. }
  134. static int gh_dbgfs_trace_class_get(void *data, u64 *val)
  135. {
  136. *val = 0;
  137. return gh_error_remap(gh_hcall_trace_update_class_flags(0, 0, val));
  138. }
  139. static int gh_dbgfs_hyp_uart_set(void *data, u64 val)
  140. {
  141. gh_control_hyp_uart(val);
  142. return 0;
  143. }
  144. static int gh_dbgfs_hyp_uart_get(void *data, u64 *val)
  145. {
  146. *val = hyp_uart_enable;
  147. return 0;
  148. }
  149. DEFINE_DEBUGFS_ATTRIBUTE(gh_dbgfs_trace_class_set_fops,
  150. gh_dbgfs_trace_class_get,
  151. gh_dbgfs_trace_class_set,
  152. "0x%llx\n");
  153. DEFINE_DEBUGFS_ATTRIBUTE(gh_dbgfs_trace_class_clear_fops,
  154. gh_dbgfs_trace_class_get,
  155. gh_dbgfs_trace_class_clear,
  156. "0x%llx\n");
  157. DEFINE_DEBUGFS_ATTRIBUTE(gh_dbgfs_hyp_uart_ctrl_fops,
  158. gh_dbgfs_hyp_uart_get,
  159. gh_dbgfs_hyp_uart_set,
  160. "0x%llx\n");
  161. static int __init gh_dbgfs_register(void)
  162. {
  163. struct dentry *dentry;
  164. gh_dbgfs_dir = debugfs_create_dir("gunyah", NULL);
  165. if (IS_ERR_OR_NULL(gh_dbgfs_dir))
  166. return PTR_ERR(gh_dbgfs_dir);
  167. if (GH_IDENTIFY_TRACE_CTRL(gunyah_api.flags[0])) {
  168. dentry = debugfs_create_file("trace_set", 0600, gh_dbgfs_dir,
  169. NULL, &gh_dbgfs_trace_class_set_fops);
  170. if (IS_ERR(dentry))
  171. return PTR_ERR(dentry);
  172. dentry = debugfs_create_file("trace_clear", 0600, gh_dbgfs_dir,
  173. NULL, &gh_dbgfs_trace_class_clear_fops);
  174. if (IS_ERR(dentry))
  175. return PTR_ERR(dentry);
  176. dentry = debugfs_create_file("hyp_uart_ctrl", 0600, gh_dbgfs_dir,
  177. NULL, &gh_dbgfs_hyp_uart_ctrl_fops);
  178. if (IS_ERR(dentry))
  179. return PTR_ERR(dentry);
  180. }
  181. return 0;
  182. }
  183. static void __exit gh_dbgfs_unregister(void)
  184. {
  185. debugfs_remove_recursive(gh_dbgfs_dir);
  186. }
  187. #else /* !defined (CONFIG_DEBUG_FS) */
  188. static inline int gh_dbgfs_register(void) { return 0; }
  189. static inline int gh_dbgfs_unregister(void) { return 0; }
  190. #endif
  191. static int __init gh_ctrl_init(void)
  192. {
  193. int ret;
  194. struct device_node *hyp;
  195. struct arm_smccc_res res;
  196. hyp = of_find_node_by_path("/hypervisor");
  197. if (!hyp || (!of_device_is_compatible(hyp, "qcom,gunyah-hypervisor") &&
  198. !of_device_is_compatible(hyp, "qcom,haven-hypervisor"))) {
  199. pr_err("gunyah-hypervisor or haven-hypervisor node not present\n");
  200. return 0;
  201. }
  202. (void)gh_hcall_hyp_identify(&gunyah_api);
  203. if (GH_API_INFO_API_VERSION(gunyah_api.api_info) != 1) {
  204. pr_err("unknown version\n");
  205. return 0;
  206. }
  207. /* Check for ARM SMCCC VENDOR_HYP service calls by UID. */
  208. arm_smccc_1_1_smc(QC_HYP_SMCCC_CALL_UID, &res);
  209. if ((res.a0 == QC_HYP_UID0) && (res.a1 == QC_HYP_UID1) &&
  210. (res.a2 == QC_HYP_UID2) && (res.a3 == QC_HYP_UID3))
  211. qc_hyp_calls = true;
  212. else if ((res.a0 == QC_HYP1_UID0) && (res.a1 == QC_HYP1_UID1) &&
  213. (res.a2 == QC_HYP1_UID2) && (res.a3 == QC_HYP1_UID3))
  214. qc_hyp_calls = true;
  215. if (qc_hyp_calls) {
  216. ret = gh_sysfs_register();
  217. if (ret)
  218. return ret;
  219. ret = gh_dbgfs_register();
  220. if (ret)
  221. pr_warn("failed to register dbgfs: %d\n", ret);
  222. } else {
  223. pr_info("Gunyah: no QC HYP interface detected\n");
  224. }
  225. return 0;
  226. }
  227. module_init(gh_ctrl_init);
  228. static void __exit gh_ctrl_exit(void)
  229. {
  230. gh_sysfs_unregister();
  231. gh_dbgfs_unregister();
  232. }
  233. module_exit(gh_ctrl_exit);
  234. MODULE_LICENSE("GPL");
  235. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Gunyah Hypervisor Control Driver");