• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Support for Partition Mobility/Migration
3  *
4  * Copyright (C) 2010 Nathan Fontenot
5  * Copyright (C) 2010 IBM Corporation
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License version
9  * 2 as published by the Free Software Foundation.
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/kobject.h>
14 #include <linux/smp.h>
15 #include <linux/stat.h>
16 #include <linux/completion.h>
17 #include <linux/device.h>
18 #include <linux/delay.h>
19 #include <linux/slab.h>
20 
21 #include <asm/rtas.h>
22 #include "pseries.h"
23 
24 static struct kobject *mobility_kobj;
25 
26 struct update_props_workarea {
27 	u32 phandle;
28 	u32 state;
29 	u64 reserved;
30 	u32 nprops;
31 };
32 
33 #define NODE_ACTION_MASK	0xff000000
34 #define NODE_COUNT_MASK		0x00ffffff
35 
36 #define DELETE_DT_NODE	0x01000000
37 #define UPDATE_DT_NODE	0x02000000
38 #define ADD_DT_NODE	0x03000000
39 
40 #define MIGRATION_SCOPE	(1)
41 
mobility_rtas_call(int token,char * buf,s32 scope)42 static int mobility_rtas_call(int token, char *buf, s32 scope)
43 {
44 	int rc;
45 
46 	spin_lock(&rtas_data_buf_lock);
47 
48 	memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
49 	rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope);
50 	memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
51 
52 	spin_unlock(&rtas_data_buf_lock);
53 	return rc;
54 }
55 
delete_dt_node(u32 phandle)56 static int delete_dt_node(u32 phandle)
57 {
58 	struct device_node *dn;
59 
60 	dn = of_find_node_by_phandle(phandle);
61 	if (!dn)
62 		return -ENOENT;
63 
64 	dlpar_detach_node(dn);
65 	return 0;
66 }
67 
update_dt_property(struct device_node * dn,struct property ** prop,const char * name,u32 vd,char * value)68 static int update_dt_property(struct device_node *dn, struct property **prop,
69 			      const char *name, u32 vd, char *value)
70 {
71 	struct property *new_prop = *prop;
72 	int more = 0;
73 
74 	/* A negative 'vd' value indicates that only part of the new property
75 	 * value is contained in the buffer and we need to call
76 	 * ibm,update-properties again to get the rest of the value.
77 	 *
78 	 * A negative value is also the two's compliment of the actual value.
79 	 */
80 	if (vd & 0x80000000) {
81 		vd = ~vd + 1;
82 		more = 1;
83 	}
84 
85 	if (new_prop) {
86 		/* partial property fixup */
87 		char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
88 		if (!new_data)
89 			return -ENOMEM;
90 
91 		memcpy(new_data, new_prop->value, new_prop->length);
92 		memcpy(new_data + new_prop->length, value, vd);
93 
94 		kfree(new_prop->value);
95 		new_prop->value = new_data;
96 		new_prop->length += vd;
97 	} else {
98 		new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
99 		if (!new_prop)
100 			return -ENOMEM;
101 
102 		new_prop->name = kstrdup(name, GFP_KERNEL);
103 		if (!new_prop->name) {
104 			kfree(new_prop);
105 			return -ENOMEM;
106 		}
107 
108 		new_prop->length = vd;
109 		new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
110 		if (!new_prop->value) {
111 			kfree(new_prop->name);
112 			kfree(new_prop);
113 			return -ENOMEM;
114 		}
115 
116 		memcpy(new_prop->value, value, vd);
117 		*prop = new_prop;
118 	}
119 
120 	if (!more) {
121 		of_update_property(dn, new_prop);
122 		new_prop = NULL;
123 	}
124 
125 	return 0;
126 }
127 
update_dt_node(u32 phandle,s32 scope)128 static int update_dt_node(u32 phandle, s32 scope)
129 {
130 	struct update_props_workarea *upwa;
131 	struct device_node *dn;
132 	struct property *prop = NULL;
133 	int i, rc;
134 	char *prop_data;
135 	char *rtas_buf;
136 	int update_properties_token;
137 	u32 vd;
138 
139 	update_properties_token = rtas_token("ibm,update-properties");
140 	if (update_properties_token == RTAS_UNKNOWN_SERVICE)
141 		return -EINVAL;
142 
143 	rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
144 	if (!rtas_buf)
145 		return -ENOMEM;
146 
147 	dn = of_find_node_by_phandle(phandle);
148 	if (!dn) {
149 		kfree(rtas_buf);
150 		return -ENOENT;
151 	}
152 
153 	upwa = (struct update_props_workarea *)&rtas_buf[0];
154 	upwa->phandle = phandle;
155 
156 	do {
157 		rc = mobility_rtas_call(update_properties_token, rtas_buf,
158 					scope);
159 		if (rc < 0)
160 			break;
161 
162 		prop_data = rtas_buf + sizeof(*upwa);
163 
164 		/* The first element of the buffer is the path of the node
165 		 * being updated in the form of a 8 byte string length
166 		 * followed by the string. Skip past this to get to the
167 		 * properties being updated.
168 		 */
169 		vd = *prop_data++;
170 		prop_data += vd;
171 
172 		/* The path we skipped over is counted as one of the elements
173 		 * returned so start counting at one.
174 		 */
175 		for (i = 1; i < upwa->nprops; i++) {
176 			char *prop_name;
177 
178 			prop_name = prop_data;
179 			prop_data += strlen(prop_name) + 1;
180 			vd = *(u32 *)prop_data;
181 			prop_data += sizeof(vd);
182 
183 			switch (vd) {
184 			case 0x00000000:
185 				/* name only property, nothing to do */
186 				break;
187 
188 			case 0x80000000:
189 				prop = of_find_property(dn, prop_name, NULL);
190 				of_remove_property(dn, prop);
191 				prop = NULL;
192 				break;
193 
194 			default:
195 				rc = update_dt_property(dn, &prop, prop_name,
196 							vd, prop_data);
197 				if (rc) {
198 					printk(KERN_ERR "Could not update %s"
199 					       " property\n", prop_name);
200 				}
201 
202 				prop_data += vd;
203 			}
204 		}
205 	} while (rc == 1);
206 
207 	of_node_put(dn);
208 	kfree(rtas_buf);
209 	return 0;
210 }
211 
add_dt_node(u32 parent_phandle,u32 drc_index)212 static int add_dt_node(u32 parent_phandle, u32 drc_index)
213 {
214 	struct device_node *dn;
215 	struct device_node *parent_dn;
216 	int rc;
217 
218 	dn = dlpar_configure_connector(drc_index);
219 	if (!dn)
220 		return -ENOENT;
221 
222 	parent_dn = of_find_node_by_phandle(parent_phandle);
223 	if (!parent_dn) {
224 		dlpar_free_cc_nodes(dn);
225 		return -ENOENT;
226 	}
227 
228 	dn->parent = parent_dn;
229 	rc = dlpar_attach_node(dn);
230 	if (rc)
231 		dlpar_free_cc_nodes(dn);
232 
233 	of_node_put(parent_dn);
234 	return rc;
235 }
236 
pseries_devicetree_update(s32 scope)237 int pseries_devicetree_update(s32 scope)
238 {
239 	char *rtas_buf;
240 	u32 *data;
241 	int update_nodes_token;
242 	int rc;
243 
244 	update_nodes_token = rtas_token("ibm,update-nodes");
245 	if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
246 		return -EINVAL;
247 
248 	rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
249 	if (!rtas_buf)
250 		return -ENOMEM;
251 
252 	do {
253 		rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope);
254 		if (rc && rc != 1)
255 			break;
256 
257 		data = (u32 *)rtas_buf + 4;
258 		while (*data & NODE_ACTION_MASK) {
259 			int i;
260 			u32 action = *data & NODE_ACTION_MASK;
261 			int node_count = *data & NODE_COUNT_MASK;
262 
263 			data++;
264 
265 			for (i = 0; i < node_count; i++) {
266 				u32 phandle = *data++;
267 				u32 drc_index;
268 
269 				switch (action) {
270 				case DELETE_DT_NODE:
271 					delete_dt_node(phandle);
272 					break;
273 				case UPDATE_DT_NODE:
274 					update_dt_node(phandle, scope);
275 					break;
276 				case ADD_DT_NODE:
277 					drc_index = *data++;
278 					add_dt_node(phandle, drc_index);
279 					break;
280 				}
281 			}
282 		}
283 	} while (rc == 1);
284 
285 	kfree(rtas_buf);
286 	return rc;
287 }
288 
post_mobility_fixup(void)289 void post_mobility_fixup(void)
290 {
291 	int rc;
292 	int activate_fw_token;
293 
294 	rc = pseries_devicetree_update(MIGRATION_SCOPE);
295 	if (rc) {
296 		printk(KERN_ERR "Initial post-mobility device tree update "
297 		       "failed: %d\n", rc);
298 		return;
299 	}
300 
301 	activate_fw_token = rtas_token("ibm,activate-firmware");
302 	if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
303 		printk(KERN_ERR "Could not make post-mobility "
304 		       "activate-fw call.\n");
305 		return;
306 	}
307 
308 	rc = rtas_call(activate_fw_token, 0, 1, NULL);
309 	if (!rc) {
310 		rc = pseries_devicetree_update(MIGRATION_SCOPE);
311 		if (rc)
312 			printk(KERN_ERR "Secondary post-mobility device tree "
313 			       "update failed: %d\n", rc);
314 	} else {
315 		printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
316 		return;
317 	}
318 
319 	return;
320 }
321 
migrate_store(struct class * class,struct class_attribute * attr,const char * buf,size_t count)322 static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
323 			     const char *buf, size_t count)
324 {
325 	struct rtas_args args;
326 	u64 streamid;
327 	int rc;
328 
329 	rc = strict_strtoull(buf, 0, &streamid);
330 	if (rc)
331 		return rc;
332 
333 	memset(&args, 0, sizeof(args));
334 	args.token = rtas_token("ibm,suspend-me");
335 	args.nargs = 2;
336 	args.nret = 1;
337 
338 	args.args[0] = streamid >> 32 ;
339 	args.args[1] = streamid & 0xffffffff;
340 	args.rets = &args.args[args.nargs];
341 
342 	do {
343 		args.rets[0] = 0;
344 		rc = rtas_ibm_suspend_me(&args);
345 		if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)
346 			ssleep(1);
347 	} while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE);
348 
349 	if (rc)
350 		return rc;
351 	else if (args.rets[0])
352 		return args.rets[0];
353 
354 	post_mobility_fixup();
355 	return count;
356 }
357 
358 static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);
359 
mobility_sysfs_init(void)360 static int __init mobility_sysfs_init(void)
361 {
362 	int rc;
363 
364 	mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
365 	if (!mobility_kobj)
366 		return -ENOMEM;
367 
368 	rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
369 
370 	return rc;
371 }
372 device_initcall(mobility_sysfs_init);
373