brintos

brintos / linux-shallow public Read only

0
0
Text · 12.3 KiB · eee9dec Raw
539 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/kernel.h>3#include <linux/fs.h>4#include <linux/semaphore.h>5#include <linux/slab.h>6#include <linux/uaccess.h>7#include <linux/of.h>8#include <asm/rtas.h>9 10#include "cxl.h"11#include "hcalls.h"12 13#define DOWNLOAD_IMAGE 114#define VALIDATE_IMAGE 215 16struct ai_header {17	u16 version;18	u8  reserved0[6];19	u16 vendor;20	u16 device;21	u16 subsystem_vendor;22	u16 subsystem;23	u64 image_offset;24	u64 image_length;25	u8  reserved1[96];26};27 28static struct semaphore sem;29static unsigned long *buffer[CXL_AI_MAX_ENTRIES];30static struct sg_list *le;31static u64 continue_token;32static unsigned int transfer;33 34struct update_props_workarea {35	__be32 phandle;36	__be32 state;37	__be64 reserved;38	__be32 nprops;39} __packed;40 41struct update_nodes_workarea {42	__be32 state;43	__be64 unit_address;44	__be32 reserved;45} __packed;46 47#define DEVICE_SCOPE 348#define NODE_ACTION_MASK	0xff00000049#define NODE_COUNT_MASK		0x00ffffff50#define OPCODE_DELETE	0x0100000051#define OPCODE_UPDATE	0x0200000052#define OPCODE_ADD	0x0300000053 54static int rcall(int token, char *buf, s32 scope)55{56	int rc;57 58	spin_lock(&rtas_data_buf_lock);59 60	memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);61	rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);62	memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);63 64	spin_unlock(&rtas_data_buf_lock);65	return rc;66}67 68static int update_property(struct device_node *dn, const char *name,69			   u32 vd, char *value)70{71	struct property *new_prop;72	u32 *val;73	int rc;74 75	new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);76	if (!new_prop)77		return -ENOMEM;78 79	new_prop->name = kstrdup(name, GFP_KERNEL);80	if (!new_prop->name) {81		kfree(new_prop);82		return -ENOMEM;83	}84 85	new_prop->length = vd;86	new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);87	if (!new_prop->value) {88		kfree(new_prop->name);89		kfree(new_prop);90		return -ENOMEM;91	}92	memcpy(new_prop->value, value, vd);93 94	val = (u32 *)new_prop->value;95	rc = cxl_update_properties(dn, new_prop);96	pr_devel("%pOFn: update property (%s, length: %i, value: %#x)\n",97		  dn, name, vd, be32_to_cpu(*val));98 99	if (rc) {100		kfree(new_prop->name);101		kfree(new_prop->value);102		kfree(new_prop);103	}104	return rc;105}106 107static int update_node(__be32 phandle, s32 scope)108{109	struct update_props_workarea *upwa;110	struct device_node *dn;111	int i, rc, ret;112	char *prop_data;113	char *buf;114	int token;115	u32 nprops;116	u32 vd;117 118	token = rtas_token("ibm,update-properties");119	if (token == RTAS_UNKNOWN_SERVICE)120		return -EINVAL;121 122	buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);123	if (!buf)124		return -ENOMEM;125 126	dn = of_find_node_by_phandle(be32_to_cpu(phandle));127	if (!dn) {128		kfree(buf);129		return -ENOENT;130	}131 132	upwa = (struct update_props_workarea *)&buf[0];133	upwa->phandle = phandle;134	do {135		rc = rcall(token, buf, scope);136		if (rc < 0)137			break;138 139		prop_data = buf + sizeof(*upwa);140		nprops = be32_to_cpu(upwa->nprops);141 142		if (*prop_data == 0) {143			prop_data++;144			vd = be32_to_cpu(*(__be32 *)prop_data);145			prop_data += vd + sizeof(vd);146			nprops--;147		}148 149		for (i = 0; i < nprops; i++) {150			char *prop_name;151 152			prop_name = prop_data;153			prop_data += strlen(prop_name) + 1;154			vd = be32_to_cpu(*(__be32 *)prop_data);155			prop_data += sizeof(vd);156 157			if ((vd != 0x00000000) && (vd != 0x80000000)) {158				ret = update_property(dn, prop_name, vd,159						prop_data);160				if (ret)161					pr_err("cxl: Could not update property %s - %i\n",162					       prop_name, ret);163 164				prop_data += vd;165			}166		}167	} while (rc == 1);168 169	of_node_put(dn);170	kfree(buf);171	return rc;172}173 174static int update_devicetree(struct cxl *adapter, s32 scope)175{176	struct update_nodes_workarea *unwa;177	u32 action, node_count;178	int token, rc, i;179	__be32 *data, phandle;180	char *buf;181 182	token = rtas_token("ibm,update-nodes");183	if (token == RTAS_UNKNOWN_SERVICE)184		return -EINVAL;185 186	buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);187	if (!buf)188		return -ENOMEM;189 190	unwa = (struct update_nodes_workarea *)&buf[0];191	unwa->unit_address = cpu_to_be64(adapter->guest->handle);192	do {193		rc = rcall(token, buf, scope);194		if (rc && rc != 1)195			break;196 197		data = (__be32 *)buf + 4;198		while (be32_to_cpu(*data) & NODE_ACTION_MASK) {199			action = be32_to_cpu(*data) & NODE_ACTION_MASK;200			node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;201			pr_devel("device reconfiguration - action: %#x, nodes: %#x\n",202				 action, node_count);203			data++;204 205			for (i = 0; i < node_count; i++) {206				phandle = *data++;207 208				switch (action) {209				case OPCODE_DELETE:210					/* nothing to do */211					break;212				case OPCODE_UPDATE:213					update_node(phandle, scope);214					break;215				case OPCODE_ADD:216					/* nothing to do, just move pointer */217					data++;218					break;219				}220			}221		}222	} while (rc == 1);223 224	kfree(buf);225	return 0;226}227 228static int handle_image(struct cxl *adapter, int operation,229			long (*fct)(u64, u64, u64, u64 *),230			struct cxl_adapter_image *ai)231{232	size_t mod, s_copy, len_chunk = 0;233	struct ai_header *header = NULL;234	unsigned int entries = 0, i;235	void *dest, *from;236	int rc = 0, need_header;237 238	/* base adapter image header */239	need_header = (ai->flags & CXL_AI_NEED_HEADER);240	if (need_header) {241		header = kzalloc(sizeof(struct ai_header), GFP_KERNEL);242		if (!header)243			return -ENOMEM;244		header->version = cpu_to_be16(1);245		header->vendor = cpu_to_be16(adapter->guest->vendor);246		header->device = cpu_to_be16(adapter->guest->device);247		header->subsystem_vendor = cpu_to_be16(adapter->guest->subsystem_vendor);248		header->subsystem = cpu_to_be16(adapter->guest->subsystem);249		header->image_offset = cpu_to_be64(CXL_AI_HEADER_SIZE);250		header->image_length = cpu_to_be64(ai->len_image);251	}252 253	/* number of entries in the list */254	len_chunk = ai->len_data;255	if (need_header)256		len_chunk += CXL_AI_HEADER_SIZE;257 258	entries = len_chunk / CXL_AI_BUFFER_SIZE;259	mod = len_chunk % CXL_AI_BUFFER_SIZE;260	if (mod)261		entries++;262 263	if (entries > CXL_AI_MAX_ENTRIES) {264		rc = -EINVAL;265		goto err;266	}267 268	/*          < -- MAX_CHUNK_SIZE = 4096 * 256 = 1048576 bytes -->269	 * chunk 0  ----------------------------------------------------270	 *          | header   |  data                                 |271	 *          ----------------------------------------------------272	 * chunk 1  ----------------------------------------------------273	 *          | data                                             |274	 *          ----------------------------------------------------275	 * ....276	 * chunk n  ----------------------------------------------------277	 *          | data                                             |278	 *          ----------------------------------------------------279	 */280	from = (void *) ai->data;281	for (i = 0; i < entries; i++) {282		dest = buffer[i];283		s_copy = CXL_AI_BUFFER_SIZE;284 285		if ((need_header) && (i == 0)) {286			/* add adapter image header */287			memcpy(buffer[i], header, sizeof(struct ai_header));288			s_copy = CXL_AI_BUFFER_SIZE - CXL_AI_HEADER_SIZE;289			dest += CXL_AI_HEADER_SIZE; /* image offset */290		}291		if ((i == (entries - 1)) && mod)292			s_copy = mod;293 294		/* copy data */295		if (copy_from_user(dest, from, s_copy))296			goto err;297 298		/* fill in the list */299		le[i].phys_addr = cpu_to_be64(virt_to_phys(buffer[i]));300		le[i].len = cpu_to_be64(CXL_AI_BUFFER_SIZE);301		if ((i == (entries - 1)) && mod)302			le[i].len = cpu_to_be64(mod);303		from += s_copy;304	}305	pr_devel("%s (op: %i, need header: %i, entries: %i, token: %#llx)\n",306		 __func__, operation, need_header, entries, continue_token);307 308	/*309	 * download/validate the adapter image to the coherent310	 * platform facility311	 */312	rc = fct(adapter->guest->handle, virt_to_phys(le), entries,313		&continue_token);314	if (rc == 0) /* success of download/validation operation */315		continue_token = 0;316 317err:318	kfree(header);319 320	return rc;321}322 323static int transfer_image(struct cxl *adapter, int operation,324			struct cxl_adapter_image *ai)325{326	int rc = 0;327	int afu;328 329	switch (operation) {330	case DOWNLOAD_IMAGE:331		rc = handle_image(adapter, operation,332				&cxl_h_download_adapter_image, ai);333		if (rc < 0) {334			pr_devel("resetting adapter\n");335			cxl_h_reset_adapter(adapter->guest->handle);336		}337		return rc;338 339	case VALIDATE_IMAGE:340		rc = handle_image(adapter, operation,341				&cxl_h_validate_adapter_image, ai);342		if (rc < 0) {343			pr_devel("resetting adapter\n");344			cxl_h_reset_adapter(adapter->guest->handle);345			return rc;346		}347		if (rc == 0) {348			pr_devel("remove current afu\n");349			for (afu = 0; afu < adapter->slices; afu++)350				cxl_guest_remove_afu(adapter->afu[afu]);351 352			pr_devel("resetting adapter\n");353			cxl_h_reset_adapter(adapter->guest->handle);354 355			/* The entire image has now been356			 * downloaded and the validation has357			 * been successfully performed.358			 * After that, the partition should call359			 * ibm,update-nodes and360			 * ibm,update-properties to receive the361			 * current configuration362			 */363			rc = update_devicetree(adapter, DEVICE_SCOPE);364			transfer = 1;365		}366		return rc;367	}368 369	return -EINVAL;370}371 372static long ioctl_transfer_image(struct cxl *adapter, int operation,373				struct cxl_adapter_image __user *uai)374{375	struct cxl_adapter_image ai;376 377	pr_devel("%s\n", __func__);378 379	if (copy_from_user(&ai, uai, sizeof(struct cxl_adapter_image)))380		return -EFAULT;381 382	/*383	 * Make sure reserved fields and bits are set to 0384	 */385	if (ai.reserved1 || ai.reserved2 || ai.reserved3 || ai.reserved4 ||386		(ai.flags & ~CXL_AI_ALL))387		return -EINVAL;388 389	return transfer_image(adapter, operation, &ai);390}391 392static int device_open(struct inode *inode, struct file *file)393{394	int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev);395	struct cxl *adapter;396	int rc = 0, i;397 398	pr_devel("in %s\n", __func__);399 400	BUG_ON(sizeof(struct ai_header) != CXL_AI_HEADER_SIZE);401 402	/* Allows one process to open the device by using a semaphore */403	if (down_interruptible(&sem) != 0)404		return -EPERM;405 406	if (!(adapter = get_cxl_adapter(adapter_num))) {407		rc = -ENODEV;408		goto err_unlock;409	}410 411	file->private_data = adapter;412	continue_token = 0;413	transfer = 0;414 415	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++)416		buffer[i] = NULL;417 418	/* aligned buffer containing list entries which describes up to419	 * 1 megabyte of data (256 entries of 4096 bytes each)420	 *  Logical real address of buffer 0  -  Buffer 0 length in bytes421	 *  Logical real address of buffer 1  -  Buffer 1 length in bytes422	 *  Logical real address of buffer 2  -  Buffer 2 length in bytes423	 *  ....424	 *  ....425	 *  Logical real address of buffer N  -  Buffer N length in bytes426	 */427	le = (struct sg_list *)get_zeroed_page(GFP_KERNEL);428	if (!le) {429		rc = -ENOMEM;430		goto err;431	}432 433	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) {434		buffer[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL);435		if (!buffer[i]) {436			rc = -ENOMEM;437			goto err1;438		}439	}440 441	return 0;442 443err1:444	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) {445		if (buffer[i])446			free_page((unsigned long) buffer[i]);447	}448 449	if (le)450		free_page((unsigned long) le);451err:452	put_device(&adapter->dev);453err_unlock:454	up(&sem);455 456	return rc;457}458 459static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)460{461	struct cxl *adapter = file->private_data;462 463	pr_devel("in %s\n", __func__);464 465	if (cmd == CXL_IOCTL_DOWNLOAD_IMAGE)466		return ioctl_transfer_image(adapter,467					DOWNLOAD_IMAGE,468					(struct cxl_adapter_image __user *)arg);469	else if (cmd == CXL_IOCTL_VALIDATE_IMAGE)470		return ioctl_transfer_image(adapter,471					VALIDATE_IMAGE,472					(struct cxl_adapter_image __user *)arg);473	else474		return -EINVAL;475}476 477static int device_close(struct inode *inode, struct file *file)478{479	struct cxl *adapter = file->private_data;480	int i;481 482	pr_devel("in %s\n", __func__);483 484	for (i = 0; i < CXL_AI_MAX_ENTRIES; i++) {485		if (buffer[i])486			free_page((unsigned long) buffer[i]);487	}488 489	if (le)490		free_page((unsigned long) le);491 492	up(&sem);493	put_device(&adapter->dev);494	continue_token = 0;495 496	/* reload the module */497	if (transfer)498		cxl_guest_reload_module(adapter);499	else {500		pr_devel("resetting adapter\n");501		cxl_h_reset_adapter(adapter->guest->handle);502	}503 504	transfer = 0;505	return 0;506}507 508static const struct file_operations fops = {509	.owner		= THIS_MODULE,510	.open		= device_open,511	.unlocked_ioctl	= device_ioctl,512	.compat_ioctl	= compat_ptr_ioctl,513	.release	= device_close,514};515 516void cxl_guest_remove_chardev(struct cxl *adapter)517{518	cdev_del(&adapter->guest->cdev);519}520 521int cxl_guest_add_chardev(struct cxl *adapter)522{523	dev_t devt;524	int rc;525 526	devt = MKDEV(MAJOR(cxl_get_dev()), CXL_CARD_MINOR(adapter));527	cdev_init(&adapter->guest->cdev, &fops);528	if ((rc = cdev_add(&adapter->guest->cdev, devt, 1))) {529		dev_err(&adapter->dev,530			"Unable to add chardev on adapter (card%i): %i\n",531			adapter->adapter_num, rc);532		goto err;533	}534	adapter->dev.devt = devt;535	sema_init(&sem, 1);536err:537	return rc;538}539