ramdax.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2025, Mike Rapoport, Microsoft
  4. *
  5. * Based on e820 pmem driver:
  6. * Copyright (c) 2015, Christoph Hellwig.
  7. * Copyright (c) 2015, Intel Corporation.
  8. */
  9. #include <linux/platform_device.h>
  10. #include <linux/memory_hotplug.h>
  11. #include <linux/libnvdimm.h>
  12. #include <linux/module.h>
  13. #include <linux/numa.h>
  14. #include <linux/slab.h>
  15. #include <linux/io.h>
  16. #include <linux/of.h>
  17. #include <uapi/linux/ndctl.h>
  18. #define LABEL_AREA_SIZE SZ_128K
  19. struct ramdax_dimm {
  20. struct nvdimm *nvdimm;
  21. void *label_area;
  22. };
  23. static void ramdax_remove(struct platform_device *pdev)
  24. {
  25. struct nvdimm_bus *nvdimm_bus = platform_get_drvdata(pdev);
  26. nvdimm_bus_unregister(nvdimm_bus);
  27. }
  28. static int ramdax_register_region(struct resource *res,
  29. struct nvdimm *nvdimm,
  30. struct nvdimm_bus *nvdimm_bus)
  31. {
  32. struct nd_mapping_desc mapping;
  33. struct nd_region_desc ndr_desc;
  34. struct nd_interleave_set *nd_set;
  35. int nid = phys_to_target_node(res->start);
  36. nd_set = kzalloc_obj(*nd_set);
  37. if (!nd_set)
  38. return -ENOMEM;
  39. nd_set->cookie1 = 0xcafebeefcafebeef;
  40. nd_set->cookie2 = nd_set->cookie1;
  41. nd_set->altcookie = nd_set->cookie1;
  42. memset(&mapping, 0, sizeof(mapping));
  43. mapping.nvdimm = nvdimm;
  44. mapping.start = 0;
  45. mapping.size = resource_size(res) - LABEL_AREA_SIZE;
  46. memset(&ndr_desc, 0, sizeof(ndr_desc));
  47. ndr_desc.res = res;
  48. ndr_desc.numa_node = numa_map_to_online_node(nid);
  49. ndr_desc.target_node = nid;
  50. ndr_desc.num_mappings = 1;
  51. ndr_desc.mapping = &mapping;
  52. ndr_desc.nd_set = nd_set;
  53. if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc))
  54. goto err_free_nd_set;
  55. return 0;
  56. err_free_nd_set:
  57. kfree(nd_set);
  58. return -ENXIO;
  59. }
  60. static int ramdax_register_dimm(struct resource *res, void *data)
  61. {
  62. resource_size_t start = res->start;
  63. resource_size_t size = resource_size(res);
  64. unsigned long flags = 0, cmd_mask = 0;
  65. struct nvdimm_bus *nvdimm_bus = data;
  66. struct ramdax_dimm *dimm;
  67. int err;
  68. dimm = kzalloc_obj(*dimm);
  69. if (!dimm)
  70. return -ENOMEM;
  71. dimm->label_area = memremap(start + size - LABEL_AREA_SIZE,
  72. LABEL_AREA_SIZE, MEMREMAP_WB);
  73. if (!dimm->label_area) {
  74. err = -ENOMEM;
  75. goto err_free_dimm;
  76. }
  77. set_bit(NDD_LABELING, &flags);
  78. set_bit(NDD_REGISTER_SYNC, &flags);
  79. set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
  80. set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
  81. set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
  82. dimm->nvdimm = nvdimm_create(nvdimm_bus, dimm,
  83. /* dimm_attribute_groups */ NULL,
  84. flags, cmd_mask, 0, NULL);
  85. if (!dimm->nvdimm) {
  86. err = -ENOMEM;
  87. goto err_unmap_label;
  88. }
  89. err = ramdax_register_region(res, dimm->nvdimm, nvdimm_bus);
  90. if (err)
  91. goto err_remove_nvdimm;
  92. return 0;
  93. err_remove_nvdimm:
  94. nvdimm_delete(dimm->nvdimm);
  95. err_unmap_label:
  96. memunmap(dimm->label_area);
  97. err_free_dimm:
  98. kfree(dimm);
  99. return err;
  100. }
  101. static int ramdax_get_config_size(struct nvdimm *nvdimm, int buf_len,
  102. struct nd_cmd_get_config_size *cmd)
  103. {
  104. if (sizeof(*cmd) > buf_len)
  105. return -EINVAL;
  106. *cmd = (struct nd_cmd_get_config_size){
  107. .status = 0,
  108. .config_size = LABEL_AREA_SIZE,
  109. .max_xfer = 8,
  110. };
  111. return 0;
  112. }
  113. static int ramdax_get_config_data(struct nvdimm *nvdimm, int buf_len,
  114. struct nd_cmd_get_config_data_hdr *cmd)
  115. {
  116. struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
  117. if (sizeof(*cmd) > buf_len)
  118. return -EINVAL;
  119. if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
  120. return -EINVAL;
  121. if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
  122. return -EINVAL;
  123. memcpy(cmd->out_buf, dimm->label_area + cmd->in_offset, cmd->in_length);
  124. return 0;
  125. }
  126. static int ramdax_set_config_data(struct nvdimm *nvdimm, int buf_len,
  127. struct nd_cmd_set_config_hdr *cmd)
  128. {
  129. struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
  130. if (sizeof(*cmd) > buf_len)
  131. return -EINVAL;
  132. if (struct_size(cmd, in_buf, cmd->in_length) > buf_len)
  133. return -EINVAL;
  134. if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
  135. return -EINVAL;
  136. memcpy(dimm->label_area + cmd->in_offset, cmd->in_buf, cmd->in_length);
  137. return 0;
  138. }
  139. static int ramdax_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
  140. void *buf, unsigned int buf_len)
  141. {
  142. unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
  143. if (!test_bit(cmd, &cmd_mask))
  144. return -ENOTTY;
  145. switch (cmd) {
  146. case ND_CMD_GET_CONFIG_SIZE:
  147. return ramdax_get_config_size(nvdimm, buf_len, buf);
  148. case ND_CMD_GET_CONFIG_DATA:
  149. return ramdax_get_config_data(nvdimm, buf_len, buf);
  150. case ND_CMD_SET_CONFIG_DATA:
  151. return ramdax_set_config_data(nvdimm, buf_len, buf);
  152. default:
  153. return -ENOTTY;
  154. }
  155. }
  156. static int ramdax_ctl(struct nvdimm_bus_descriptor *nd_desc,
  157. struct nvdimm *nvdimm, unsigned int cmd, void *buf,
  158. unsigned int buf_len, int *cmd_rc)
  159. {
  160. /*
  161. * No firmware response to translate, let the transport error
  162. * code take precedence.
  163. */
  164. *cmd_rc = 0;
  165. if (!nvdimm)
  166. return -ENOTTY;
  167. return ramdax_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
  168. }
  169. #ifdef CONFIG_OF
  170. static const struct of_device_id ramdax_of_matches[] = {
  171. { .compatible = "pmem-region", },
  172. { },
  173. };
  174. #endif
  175. static int ramdax_probe_of(struct platform_device *pdev,
  176. struct nvdimm_bus *bus, struct device_node *np)
  177. {
  178. int err;
  179. if (!of_match_node(ramdax_of_matches, np))
  180. return -ENODEV;
  181. for (int i = 0; i < pdev->num_resources; i++) {
  182. err = ramdax_register_dimm(&pdev->resource[i], bus);
  183. if (err)
  184. goto err_unregister;
  185. }
  186. return 0;
  187. err_unregister:
  188. /*
  189. * FIXME: should we unregister the dimms that were registered
  190. * successfully
  191. */
  192. return err;
  193. }
  194. static int ramdax_probe(struct platform_device *pdev)
  195. {
  196. static struct nvdimm_bus_descriptor nd_desc;
  197. struct device *dev = &pdev->dev;
  198. struct nvdimm_bus *nvdimm_bus;
  199. struct device_node *np;
  200. int rc = -ENXIO;
  201. nd_desc.provider_name = "ramdax";
  202. nd_desc.module = THIS_MODULE;
  203. nd_desc.ndctl = ramdax_ctl;
  204. nvdimm_bus = nvdimm_bus_register(dev, &nd_desc);
  205. if (!nvdimm_bus)
  206. goto err;
  207. np = dev_of_node(&pdev->dev);
  208. if (np)
  209. rc = ramdax_probe_of(pdev, nvdimm_bus, np);
  210. else
  211. rc = walk_iomem_res_desc(IORES_DESC_PERSISTENT_MEMORY_LEGACY,
  212. IORESOURCE_MEM, 0, -1, nvdimm_bus,
  213. ramdax_register_dimm);
  214. if (rc)
  215. goto err;
  216. platform_set_drvdata(pdev, nvdimm_bus);
  217. return 0;
  218. err:
  219. nvdimm_bus_unregister(nvdimm_bus);
  220. return rc;
  221. }
  222. static struct platform_driver ramdax_driver = {
  223. .probe = ramdax_probe,
  224. .remove = ramdax_remove,
  225. .driver = {
  226. .name = "ramdax",
  227. },
  228. };
  229. module_platform_driver(ramdax_driver);
  230. MODULE_DESCRIPTION("NVDIMM support for e820 type-12 memory and OF pmem-region");
  231. MODULE_LICENSE("GPL");
  232. MODULE_AUTHOR("Microsoft Corporation");