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 static int mobility_rtas_call(int token, char *buf) 41 { 42 int rc; 43 44 spin_lock(&rtas_data_buf_lock); 45 46 memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); 47 rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1); 48 memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 49 50 spin_unlock(&rtas_data_buf_lock); 51 return rc; 52 } 53 54 static int delete_dt_node(u32 phandle) 55 { 56 struct device_node *dn; 57 58 dn = of_find_node_by_phandle(phandle); 59 if (!dn) 60 return -ENOENT; 61 62 dlpar_detach_node(dn); 63 return 0; 64 } 65 66 static int update_dt_property(struct device_node *dn, struct property **prop, 67 const char *name, u32 vd, char *value) 68 { 69 struct property *new_prop = *prop; 70 int more = 0; 71 72 /* A negative 'vd' value indicates that only part of the new property 73 * value is contained in the buffer and we need to call 74 * ibm,update-properties again to get the rest of the value. 75 * 76 * A negative value is also the two's compliment of the actual value. 77 */ 78 if (vd & 0x80000000) { 79 vd = ~vd + 1; 80 more = 1; 81 } 82 83 if (new_prop) { 84 /* partial property fixup */ 85 char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL); 86 if (!new_data) 87 return -ENOMEM; 88 89 memcpy(new_data, new_prop->value, new_prop->length); 90 memcpy(new_data + new_prop->length, value, vd); 91 92 kfree(new_prop->value); 93 new_prop->value = new_data; 94 new_prop->length += vd; 95 } else { 96 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL); 97 if (!new_prop) 98 return -ENOMEM; 99 100 new_prop->name = kstrdup(name, GFP_KERNEL); 101 if (!new_prop->name) { 102 kfree(new_prop); 103 return -ENOMEM; 104 } 105 106 new_prop->length = vd; 107 new_prop->value = kzalloc(new_prop->length, GFP_KERNEL); 108 if (!new_prop->value) { 109 kfree(new_prop->name); 110 kfree(new_prop); 111 return -ENOMEM; 112 } 113 114 memcpy(new_prop->value, value, vd); 115 *prop = new_prop; 116 } 117 118 if (!more) { 119 prom_update_property(dn, new_prop); 120 new_prop = NULL; 121 } 122 123 return 0; 124 } 125 126 static int update_dt_node(u32 phandle) 127 { 128 struct update_props_workarea *upwa; 129 struct device_node *dn; 130 struct property *prop = NULL; 131 int i, rc; 132 char *prop_data; 133 char *rtas_buf; 134 int update_properties_token; 135 136 update_properties_token = rtas_token("ibm,update-properties"); 137 if (update_properties_token == RTAS_UNKNOWN_SERVICE) 138 return -EINVAL; 139 140 rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 141 if (!rtas_buf) 142 return -ENOMEM; 143 144 dn = of_find_node_by_phandle(phandle); 145 if (!dn) { 146 kfree(rtas_buf); 147 return -ENOENT; 148 } 149 150 upwa = (struct update_props_workarea *)&rtas_buf[0]; 151 upwa->phandle = phandle; 152 153 do { 154 rc = mobility_rtas_call(update_properties_token, rtas_buf); 155 if (rc < 0) 156 break; 157 158 prop_data = rtas_buf + sizeof(*upwa); 159 160 for (i = 0; i < upwa->nprops; i++) { 161 char *prop_name; 162 u32 vd; 163 164 prop_name = prop_data + 1; 165 prop_data += strlen(prop_name) + 1; 166 vd = *prop_data++; 167 168 switch (vd) { 169 case 0x00000000: 170 /* name only property, nothing to do */ 171 break; 172 173 case 0x80000000: 174 prop = of_find_property(dn, prop_name, NULL); 175 prom_remove_property(dn, prop); 176 prop = NULL; 177 break; 178 179 default: 180 rc = update_dt_property(dn, &prop, prop_name, 181 vd, prop_data); 182 if (rc) { 183 printk(KERN_ERR "Could not update %s" 184 " property\n", prop_name); 185 } 186 187 prop_data += vd; 188 } 189 } 190 } while (rc == 1); 191 192 of_node_put(dn); 193 kfree(rtas_buf); 194 return 0; 195 } 196 197 static int add_dt_node(u32 parent_phandle, u32 drc_index) 198 { 199 struct device_node *dn; 200 struct device_node *parent_dn; 201 int rc; 202 203 dn = dlpar_configure_connector(drc_index); 204 if (!dn) 205 return -ENOENT; 206 207 parent_dn = of_find_node_by_phandle(parent_phandle); 208 if (!parent_dn) { 209 dlpar_free_cc_nodes(dn); 210 return -ENOENT; 211 } 212 213 dn->parent = parent_dn; 214 rc = dlpar_attach_node(dn); 215 if (rc) 216 dlpar_free_cc_nodes(dn); 217 218 of_node_put(parent_dn); 219 return rc; 220 } 221 222 static int pseries_devicetree_update(void) 223 { 224 char *rtas_buf; 225 u32 *data; 226 int update_nodes_token; 227 int rc; 228 229 update_nodes_token = rtas_token("ibm,update-nodes"); 230 if (update_nodes_token == RTAS_UNKNOWN_SERVICE) 231 return -EINVAL; 232 233 rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 234 if (!rtas_buf) 235 return -ENOMEM; 236 237 do { 238 rc = mobility_rtas_call(update_nodes_token, rtas_buf); 239 if (rc && rc != 1) 240 break; 241 242 data = (u32 *)rtas_buf + 4; 243 while (*data & NODE_ACTION_MASK) { 244 int i; 245 u32 action = *data & NODE_ACTION_MASK; 246 int node_count = *data & NODE_COUNT_MASK; 247 248 data++; 249 250 for (i = 0; i < node_count; i++) { 251 u32 phandle = *data++; 252 u32 drc_index; 253 254 switch (action) { 255 case DELETE_DT_NODE: 256 delete_dt_node(phandle); 257 break; 258 case UPDATE_DT_NODE: 259 update_dt_node(phandle); 260 break; 261 case ADD_DT_NODE: 262 drc_index = *data++; 263 add_dt_node(phandle, drc_index); 264 break; 265 } 266 } 267 } 268 } while (rc == 1); 269 270 kfree(rtas_buf); 271 return rc; 272 } 273 274 void post_mobility_fixup(void) 275 { 276 int rc; 277 int activate_fw_token; 278 279 rc = pseries_devicetree_update(); 280 if (rc) { 281 printk(KERN_ERR "Initial post-mobility device tree update " 282 "failed: %d\n", rc); 283 return; 284 } 285 286 activate_fw_token = rtas_token("ibm,activate-firmware"); 287 if (activate_fw_token == RTAS_UNKNOWN_SERVICE) { 288 printk(KERN_ERR "Could not make post-mobility " 289 "activate-fw call.\n"); 290 return; 291 } 292 293 rc = rtas_call(activate_fw_token, 0, 1, NULL); 294 if (!rc) { 295 rc = pseries_devicetree_update(); 296 if (rc) 297 printk(KERN_ERR "Secondary post-mobility device tree " 298 "update failed: %d\n", rc); 299 } else { 300 printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc); 301 return; 302 } 303 304 return; 305 } 306 307 static ssize_t migrate_store(struct class *class, struct class_attribute *attr, 308 const char *buf, size_t count) 309 { 310 struct rtas_args args; 311 u64 streamid; 312 int rc; 313 314 rc = strict_strtoull(buf, 0, &streamid); 315 if (rc) 316 return rc; 317 318 memset(&args, 0, sizeof(args)); 319 args.token = rtas_token("ibm,suspend-me"); 320 args.nargs = 2; 321 args.nret = 1; 322 323 args.args[0] = streamid >> 32 ; 324 args.args[1] = streamid & 0xffffffff; 325 args.rets = &args.args[args.nargs]; 326 327 do { 328 args.rets[0] = 0; 329 rc = rtas_ibm_suspend_me(&args); 330 if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE) 331 ssleep(1); 332 } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE); 333 334 if (rc) 335 return rc; 336 else if (args.rets[0]) 337 return args.rets[0]; 338 339 post_mobility_fixup(); 340 return count; 341 } 342 343 static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store); 344 345 static int __init mobility_sysfs_init(void) 346 { 347 int rc; 348 349 mobility_kobj = kobject_create_and_add("mobility", kernel_kobj); 350 if (!mobility_kobj) 351 return -ENOMEM; 352 353 rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr); 354 355 return rc; 356 } 357 device_initcall(mobility_sysfs_init); 358