cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pmem.c (9191B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
      3#include <linux/libnvdimm.h>
      4#include <asm/unaligned.h>
      5#include <linux/device.h>
      6#include <linux/module.h>
      7#include <linux/ndctl.h>
      8#include <linux/async.h>
      9#include <linux/slab.h>
     10#include "cxlmem.h"
     11#include "cxl.h"
     12
     13/*
     14 * Ordered workqueue for cxl nvdimm device arrival and departure
     15 * to coordinate bus rescans when a bridge arrives and trigger remove
     16 * operations when the bridge is removed.
     17 */
     18static struct workqueue_struct *cxl_pmem_wq;
     19
     20static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
     21
     22static void clear_exclusive(void *cxlds)
     23{
     24	clear_exclusive_cxl_commands(cxlds, exclusive_cmds);
     25}
     26
     27static void unregister_nvdimm(void *nvdimm)
     28{
     29	nvdimm_delete(nvdimm);
     30}
     31
     32static int cxl_nvdimm_probe(struct device *dev)
     33{
     34	struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
     35	struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
     36	unsigned long flags = 0, cmd_mask = 0;
     37	struct cxl_dev_state *cxlds = cxlmd->cxlds;
     38	struct cxl_nvdimm_bridge *cxl_nvb;
     39	struct nvdimm *nvdimm;
     40	int rc;
     41
     42	cxl_nvb = cxl_find_nvdimm_bridge(cxl_nvd);
     43	if (!cxl_nvb)
     44		return -ENXIO;
     45
     46	device_lock(&cxl_nvb->dev);
     47	if (!cxl_nvb->nvdimm_bus) {
     48		rc = -ENXIO;
     49		goto out;
     50	}
     51
     52	set_exclusive_cxl_commands(cxlds, exclusive_cmds);
     53	rc = devm_add_action_or_reset(dev, clear_exclusive, cxlds);
     54	if (rc)
     55		goto out;
     56
     57	set_bit(NDD_LABELING, &flags);
     58	set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
     59	set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
     60	set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
     61	nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags,
     62			       cmd_mask, 0, NULL);
     63	if (!nvdimm) {
     64		rc = -ENOMEM;
     65		goto out;
     66	}
     67
     68	dev_set_drvdata(dev, nvdimm);
     69	rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
     70out:
     71	device_unlock(&cxl_nvb->dev);
     72	put_device(&cxl_nvb->dev);
     73
     74	return rc;
     75}
     76
     77static struct cxl_driver cxl_nvdimm_driver = {
     78	.name = "cxl_nvdimm",
     79	.probe = cxl_nvdimm_probe,
     80	.id = CXL_DEVICE_NVDIMM,
     81};
     82
     83static int cxl_pmem_get_config_size(struct cxl_dev_state *cxlds,
     84				    struct nd_cmd_get_config_size *cmd,
     85				    unsigned int buf_len)
     86{
     87	if (sizeof(*cmd) > buf_len)
     88		return -EINVAL;
     89
     90	*cmd = (struct nd_cmd_get_config_size) {
     91		 .config_size = cxlds->lsa_size,
     92		 .max_xfer = cxlds->payload_size,
     93	};
     94
     95	return 0;
     96}
     97
     98static int cxl_pmem_get_config_data(struct cxl_dev_state *cxlds,
     99				    struct nd_cmd_get_config_data_hdr *cmd,
    100				    unsigned int buf_len)
    101{
    102	struct cxl_mbox_get_lsa get_lsa;
    103	int rc;
    104
    105	if (sizeof(*cmd) > buf_len)
    106		return -EINVAL;
    107	if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
    108		return -EINVAL;
    109
    110	get_lsa = (struct cxl_mbox_get_lsa) {
    111		.offset = cpu_to_le32(cmd->in_offset),
    112		.length = cpu_to_le32(cmd->in_length),
    113	};
    114
    115	rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_LSA, &get_lsa,
    116			       sizeof(get_lsa), cmd->out_buf, cmd->in_length);
    117	cmd->status = 0;
    118
    119	return rc;
    120}
    121
    122static int cxl_pmem_set_config_data(struct cxl_dev_state *cxlds,
    123				    struct nd_cmd_set_config_hdr *cmd,
    124				    unsigned int buf_len)
    125{
    126	struct cxl_mbox_set_lsa *set_lsa;
    127	int rc;
    128
    129	if (sizeof(*cmd) > buf_len)
    130		return -EINVAL;
    131
    132	/* 4-byte status follows the input data in the payload */
    133	if (struct_size(cmd, in_buf, cmd->in_length) + 4 > buf_len)
    134		return -EINVAL;
    135
    136	set_lsa =
    137		kvzalloc(struct_size(set_lsa, data, cmd->in_length), GFP_KERNEL);
    138	if (!set_lsa)
    139		return -ENOMEM;
    140
    141	*set_lsa = (struct cxl_mbox_set_lsa) {
    142		.offset = cpu_to_le32(cmd->in_offset),
    143	};
    144	memcpy(set_lsa->data, cmd->in_buf, cmd->in_length);
    145
    146	rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_SET_LSA, set_lsa,
    147			       struct_size(set_lsa, data, cmd->in_length),
    148			       NULL, 0);
    149
    150	/*
    151	 * Set "firmware" status (4-packed bytes at the end of the input
    152	 * payload.
    153	 */
    154	put_unaligned(0, (u32 *) &cmd->in_buf[cmd->in_length]);
    155	kvfree(set_lsa);
    156
    157	return rc;
    158}
    159
    160static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
    161			       void *buf, unsigned int buf_len)
    162{
    163	struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
    164	unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
    165	struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
    166	struct cxl_dev_state *cxlds = cxlmd->cxlds;
    167
    168	if (!test_bit(cmd, &cmd_mask))
    169		return -ENOTTY;
    170
    171	switch (cmd) {
    172	case ND_CMD_GET_CONFIG_SIZE:
    173		return cxl_pmem_get_config_size(cxlds, buf, buf_len);
    174	case ND_CMD_GET_CONFIG_DATA:
    175		return cxl_pmem_get_config_data(cxlds, buf, buf_len);
    176	case ND_CMD_SET_CONFIG_DATA:
    177		return cxl_pmem_set_config_data(cxlds, buf, buf_len);
    178	default:
    179		return -ENOTTY;
    180	}
    181}
    182
    183static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,
    184			struct nvdimm *nvdimm, unsigned int cmd, void *buf,
    185			unsigned int buf_len, int *cmd_rc)
    186{
    187	/*
    188	 * No firmware response to translate, let the transport error
    189	 * code take precedence.
    190	 */
    191	*cmd_rc = 0;
    192
    193	if (!nvdimm)
    194		return -ENOTTY;
    195	return cxl_pmem_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
    196}
    197
    198static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)
    199{
    200	if (cxl_nvb->nvdimm_bus)
    201		return true;
    202	cxl_nvb->nvdimm_bus =
    203		nvdimm_bus_register(&cxl_nvb->dev, &cxl_nvb->nd_desc);
    204	return cxl_nvb->nvdimm_bus != NULL;
    205}
    206
    207static int cxl_nvdimm_release_driver(struct device *dev, void *data)
    208{
    209	if (!is_cxl_nvdimm(dev))
    210		return 0;
    211	device_release_driver(dev);
    212	return 0;
    213}
    214
    215static void offline_nvdimm_bus(struct nvdimm_bus *nvdimm_bus)
    216{
    217	if (!nvdimm_bus)
    218		return;
    219
    220	/*
    221	 * Set the state of cxl_nvdimm devices to unbound / idle before
    222	 * nvdimm_bus_unregister() rips the nvdimm objects out from
    223	 * underneath them.
    224	 */
    225	bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_release_driver);
    226	nvdimm_bus_unregister(nvdimm_bus);
    227}
    228
    229static void cxl_nvb_update_state(struct work_struct *work)
    230{
    231	struct cxl_nvdimm_bridge *cxl_nvb =
    232		container_of(work, typeof(*cxl_nvb), state_work);
    233	struct nvdimm_bus *victim_bus = NULL;
    234	bool release = false, rescan = false;
    235
    236	device_lock(&cxl_nvb->dev);
    237	switch (cxl_nvb->state) {
    238	case CXL_NVB_ONLINE:
    239		if (!online_nvdimm_bus(cxl_nvb)) {
    240			dev_err(&cxl_nvb->dev,
    241				"failed to establish nvdimm bus\n");
    242			release = true;
    243		} else
    244			rescan = true;
    245		break;
    246	case CXL_NVB_OFFLINE:
    247	case CXL_NVB_DEAD:
    248		victim_bus = cxl_nvb->nvdimm_bus;
    249		cxl_nvb->nvdimm_bus = NULL;
    250		break;
    251	default:
    252		break;
    253	}
    254	device_unlock(&cxl_nvb->dev);
    255
    256	if (release)
    257		device_release_driver(&cxl_nvb->dev);
    258	if (rescan) {
    259		int rc = bus_rescan_devices(&cxl_bus_type);
    260
    261		dev_dbg(&cxl_nvb->dev, "rescan: %d\n", rc);
    262	}
    263	offline_nvdimm_bus(victim_bus);
    264
    265	put_device(&cxl_nvb->dev);
    266}
    267
    268static void cxl_nvdimm_bridge_state_work(struct cxl_nvdimm_bridge *cxl_nvb)
    269{
    270	/*
    271	 * Take a reference that the workqueue will drop if new work
    272	 * gets queued.
    273	 */
    274	get_device(&cxl_nvb->dev);
    275	if (!queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
    276		put_device(&cxl_nvb->dev);
    277}
    278
    279static void cxl_nvdimm_bridge_remove(struct device *dev)
    280{
    281	struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
    282
    283	if (cxl_nvb->state == CXL_NVB_ONLINE)
    284		cxl_nvb->state = CXL_NVB_OFFLINE;
    285	cxl_nvdimm_bridge_state_work(cxl_nvb);
    286}
    287
    288static int cxl_nvdimm_bridge_probe(struct device *dev)
    289{
    290	struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
    291
    292	if (cxl_nvb->state == CXL_NVB_DEAD)
    293		return -ENXIO;
    294
    295	if (cxl_nvb->state == CXL_NVB_NEW) {
    296		cxl_nvb->nd_desc = (struct nvdimm_bus_descriptor) {
    297			.provider_name = "CXL",
    298			.module = THIS_MODULE,
    299			.ndctl = cxl_pmem_ctl,
    300		};
    301
    302		INIT_WORK(&cxl_nvb->state_work, cxl_nvb_update_state);
    303	}
    304
    305	cxl_nvb->state = CXL_NVB_ONLINE;
    306	cxl_nvdimm_bridge_state_work(cxl_nvb);
    307
    308	return 0;
    309}
    310
    311static struct cxl_driver cxl_nvdimm_bridge_driver = {
    312	.name = "cxl_nvdimm_bridge",
    313	.probe = cxl_nvdimm_bridge_probe,
    314	.remove = cxl_nvdimm_bridge_remove,
    315	.id = CXL_DEVICE_NVDIMM_BRIDGE,
    316};
    317
    318/*
    319 * Return all bridges to the CXL_NVB_NEW state to invalidate any
    320 * ->state_work referring to the now destroyed cxl_pmem_wq.
    321 */
    322static int cxl_nvdimm_bridge_reset(struct device *dev, void *data)
    323{
    324	struct cxl_nvdimm_bridge *cxl_nvb;
    325
    326	if (!is_cxl_nvdimm_bridge(dev))
    327		return 0;
    328
    329	cxl_nvb = to_cxl_nvdimm_bridge(dev);
    330	device_lock(dev);
    331	cxl_nvb->state = CXL_NVB_NEW;
    332	device_unlock(dev);
    333
    334	return 0;
    335}
    336
    337static void destroy_cxl_pmem_wq(void)
    338{
    339	destroy_workqueue(cxl_pmem_wq);
    340	bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_bridge_reset);
    341}
    342
    343static __init int cxl_pmem_init(void)
    344{
    345	int rc;
    346
    347	set_bit(CXL_MEM_COMMAND_ID_SET_SHUTDOWN_STATE, exclusive_cmds);
    348	set_bit(CXL_MEM_COMMAND_ID_SET_LSA, exclusive_cmds);
    349
    350	cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0);
    351	if (!cxl_pmem_wq)
    352		return -ENXIO;
    353
    354	rc = cxl_driver_register(&cxl_nvdimm_bridge_driver);
    355	if (rc)
    356		goto err_bridge;
    357
    358	rc = cxl_driver_register(&cxl_nvdimm_driver);
    359	if (rc)
    360		goto err_nvdimm;
    361
    362	return 0;
    363
    364err_nvdimm:
    365	cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
    366err_bridge:
    367	destroy_cxl_pmem_wq();
    368	return rc;
    369}
    370
    371static __exit void cxl_pmem_exit(void)
    372{
    373	cxl_driver_unregister(&cxl_nvdimm_driver);
    374	cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
    375	destroy_cxl_pmem_wq();
    376}
    377
    378MODULE_LICENSE("GPL v2");
    379module_init(cxl_pmem_init);
    380module_exit(cxl_pmem_exit);
    381MODULE_IMPORT_NS(CXL);
    382MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM_BRIDGE);
    383MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM);