diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-07-12 18:55:55 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-16 07:04:36 -0400 |
commit | b3e13fbeb9ac1eb8e7b0791bf56e1775c692972b (patch) | |
tree | 06539dfe2332c98c4d8b83450fe1e6055680ddc0 /arch/sparc64/kernel/ds.c | |
parent | 83292e0a9c3f1c326b28fbf8cb70a8ce81a98163 (diff) |
[SPARC64]: Fix setting of variables in LDOM guest.
There is a special domain services capability for setting
variables in the OBP options node. Guests don't have permanent
store for the OBP variables like a normal system, so they are
instead maintained in the LDOM control node or in the SC.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/ds.c')
-rw-r--r-- | arch/sparc64/kernel/ds.c | 187 |
1 files changed, 174 insertions, 13 deletions
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index 9c8839d1cffd..4e20ef232c51 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/mutex.h> | ||
14 | 15 | ||
15 | #include <asm/ldc.h> | 16 | #include <asm/ldc.h> |
16 | #include <asm/vio.h> | 17 | #include <asm/vio.h> |
@@ -171,7 +172,7 @@ static void md_update_data(struct ldc_channel *lp, | |||
171 | 172 | ||
172 | rp = (struct ds_md_update_req *) (dpkt + 1); | 173 | rp = (struct ds_md_update_req *) (dpkt + 1); |
173 | 174 | ||
174 | printk(KERN_ERR PFX "Machine description update.\n"); | 175 | printk(KERN_INFO PFX "Machine description update.\n"); |
175 | 176 | ||
176 | memset(&pkt, 0, sizeof(pkt)); | 177 | memset(&pkt, 0, sizeof(pkt)); |
177 | pkt.data.tag.type = DS_DATA; | 178 | pkt.data.tag.type = DS_DATA; |
@@ -248,8 +249,8 @@ static void domain_panic_data(struct ldc_channel *lp, | |||
248 | 249 | ||
249 | rp = (struct ds_panic_req *) (dpkt + 1); | 250 | rp = (struct ds_panic_req *) (dpkt + 1); |
250 | 251 | ||
251 | printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n", | 252 | printk(KERN_ALERT PFX "Panic request from " |
252 | rp->req_num, len); | 253 | "LDOM manager received.\n"); |
253 | 254 | ||
254 | memset(&pkt, 0, sizeof(pkt)); | 255 | memset(&pkt, 0, sizeof(pkt)); |
255 | pkt.data.tag.type = DS_DATA; | 256 | pkt.data.tag.type = DS_DATA; |
@@ -313,10 +314,60 @@ static void ds_pri_data(struct ldc_channel *lp, | |||
313 | 314 | ||
314 | rp = (struct ds_pri_msg *) (dpkt + 1); | 315 | rp = (struct ds_pri_msg *) (dpkt + 1); |
315 | 316 | ||
316 | printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n", | 317 | printk(KERN_INFO PFX "PRI REQ [%lx:%lx], len=%d\n", |
317 | rp->req_num, rp->type, len); | 318 | rp->req_num, rp->type, len); |
318 | } | 319 | } |
319 | 320 | ||
321 | struct ds_var_hdr { | ||
322 | __u32 type; | ||
323 | #define DS_VAR_SET_REQ 0x00 | ||
324 | #define DS_VAR_DELETE_REQ 0x01 | ||
325 | #define DS_VAR_SET_RESP 0x02 | ||
326 | #define DS_VAR_DELETE_RESP 0x03 | ||
327 | }; | ||
328 | |||
329 | struct ds_var_set_msg { | ||
330 | struct ds_var_hdr hdr; | ||
331 | char name_and_value[0]; | ||
332 | }; | ||
333 | |||
334 | struct ds_var_delete_msg { | ||
335 | struct ds_var_hdr hdr; | ||
336 | char name[0]; | ||
337 | }; | ||
338 | |||
339 | struct ds_var_resp { | ||
340 | struct ds_var_hdr hdr; | ||
341 | __u32 result; | ||
342 | #define DS_VAR_SUCCESS 0x00 | ||
343 | #define DS_VAR_NO_SPACE 0x01 | ||
344 | #define DS_VAR_INVALID_VAR 0x02 | ||
345 | #define DS_VAR_INVALID_VAL 0x03 | ||
346 | #define DS_VAR_NOT_PRESENT 0x04 | ||
347 | }; | ||
348 | |||
349 | static DEFINE_MUTEX(ds_var_mutex); | ||
350 | static int ds_var_doorbell; | ||
351 | static int ds_var_response; | ||
352 | |||
353 | static void ds_var_data(struct ldc_channel *lp, | ||
354 | struct ds_cap_state *dp, | ||
355 | void *buf, int len) | ||
356 | { | ||
357 | struct ds_data *dpkt = buf; | ||
358 | struct ds_var_resp *rp; | ||
359 | |||
360 | rp = (struct ds_var_resp *) (dpkt + 1); | ||
361 | |||
362 | if (rp->hdr.type != DS_VAR_SET_RESP && | ||
363 | rp->hdr.type != DS_VAR_DELETE_RESP) | ||
364 | return; | ||
365 | |||
366 | ds_var_response = rp->result; | ||
367 | wmb(); | ||
368 | ds_var_doorbell = 1; | ||
369 | } | ||
370 | |||
320 | struct ds_cap_state ds_states[] = { | 371 | struct ds_cap_state ds_states[] = { |
321 | { | 372 | { |
322 | .service_id = "md-update", | 373 | .service_id = "md-update", |
@@ -338,17 +389,16 @@ struct ds_cap_state ds_states[] = { | |||
338 | .service_id = "pri", | 389 | .service_id = "pri", |
339 | .data = ds_pri_data, | 390 | .data = ds_pri_data, |
340 | }, | 391 | }, |
392 | { | ||
393 | .service_id = "var-config", | ||
394 | .data = ds_var_data, | ||
395 | }, | ||
396 | { | ||
397 | .service_id = "var-config-backup", | ||
398 | .data = ds_var_data, | ||
399 | }, | ||
341 | }; | 400 | }; |
342 | 401 | ||
343 | static struct ds_cap_state *find_cap(u64 handle) | ||
344 | { | ||
345 | unsigned int index = handle >> 32; | ||
346 | |||
347 | if (index >= ARRAY_SIZE(ds_states)) | ||
348 | return NULL; | ||
349 | return &ds_states[index]; | ||
350 | } | ||
351 | |||
352 | static DEFINE_SPINLOCK(ds_lock); | 402 | static DEFINE_SPINLOCK(ds_lock); |
353 | 403 | ||
354 | struct ds_info { | 404 | struct ds_info { |
@@ -361,6 +411,115 @@ struct ds_info { | |||
361 | int rcv_buf_len; | 411 | int rcv_buf_len; |
362 | }; | 412 | }; |
363 | 413 | ||
414 | static struct ds_info *ds_info; | ||
415 | |||
416 | static struct ds_cap_state *find_cap(u64 handle) | ||
417 | { | ||
418 | unsigned int index = handle >> 32; | ||
419 | |||
420 | if (index >= ARRAY_SIZE(ds_states)) | ||
421 | return NULL; | ||
422 | return &ds_states[index]; | ||
423 | } | ||
424 | |||
425 | static struct ds_cap_state *find_cap_by_string(const char *name) | ||
426 | { | ||
427 | int i; | ||
428 | |||
429 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) { | ||
430 | if (strcmp(ds_states[i].service_id, name)) | ||
431 | continue; | ||
432 | |||
433 | return &ds_states[i]; | ||
434 | } | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
438 | void ldom_set_var(const char *var, const char *value) | ||
439 | { | ||
440 | struct ds_info *dp = ds_info; | ||
441 | struct ds_cap_state *cp; | ||
442 | |||
443 | cp = find_cap_by_string("var-config"); | ||
444 | if (cp->state != CAP_STATE_REGISTERED) | ||
445 | cp = find_cap_by_string("var-config-backup"); | ||
446 | |||
447 | if (cp->state == CAP_STATE_REGISTERED) { | ||
448 | union { | ||
449 | struct { | ||
450 | struct ds_data data; | ||
451 | struct ds_var_set_msg msg; | ||
452 | } header; | ||
453 | char all[512]; | ||
454 | } pkt; | ||
455 | unsigned long flags; | ||
456 | char *base, *p; | ||
457 | int msg_len, loops; | ||
458 | |||
459 | memset(&pkt, 0, sizeof(pkt)); | ||
460 | pkt.header.data.tag.type = DS_DATA; | ||
461 | pkt.header.data.handle = cp->handle; | ||
462 | pkt.header.msg.hdr.type = DS_VAR_SET_REQ; | ||
463 | base = p = &pkt.header.msg.name_and_value[0]; | ||
464 | strcpy(p, var); | ||
465 | p += strlen(var) + 1; | ||
466 | strcpy(p, value); | ||
467 | p += strlen(value) + 1; | ||
468 | |||
469 | msg_len = (sizeof(struct ds_data) + | ||
470 | sizeof(struct ds_var_set_msg) + | ||
471 | (p - base)); | ||
472 | msg_len = (msg_len + 3) & ~3; | ||
473 | pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag); | ||
474 | |||
475 | mutex_lock(&ds_var_mutex); | ||
476 | |||
477 | spin_lock_irqsave(&ds_lock, flags); | ||
478 | ds_var_doorbell = 0; | ||
479 | ds_var_response = -1; | ||
480 | |||
481 | ds_send(dp->lp, &pkt, msg_len); | ||
482 | spin_unlock_irqrestore(&ds_lock, flags); | ||
483 | |||
484 | loops = 1000; | ||
485 | while (ds_var_doorbell == 0) { | ||
486 | if (loops-- < 0) | ||
487 | break; | ||
488 | barrier(); | ||
489 | udelay(100); | ||
490 | } | ||
491 | |||
492 | mutex_unlock(&ds_var_mutex); | ||
493 | |||
494 | if (ds_var_doorbell == 0 || | ||
495 | ds_var_response != DS_VAR_SUCCESS) | ||
496 | printk(KERN_ERR PFX "var-config [%s:%s] " | ||
497 | "failed, response(%d).\n", | ||
498 | var, value, | ||
499 | ds_var_response); | ||
500 | } else { | ||
501 | printk(KERN_ERR PFX "var-config not registered so " | ||
502 | "could not set (%s) variable to (%s).\n", | ||
503 | var, value); | ||
504 | } | ||
505 | } | ||
506 | |||
507 | void ldom_reboot(const char *boot_command) | ||
508 | { | ||
509 | /* Don't bother with any of this if the boot_command | ||
510 | * is empty. | ||
511 | */ | ||
512 | if (boot_command && strlen(boot_command)) { | ||
513 | char full_boot_str[256]; | ||
514 | |||
515 | strcpy(full_boot_str, "boot "); | ||
516 | strcpy(full_boot_str + strlen("boot "), boot_command); | ||
517 | |||
518 | ldom_set_var("reboot-command", full_boot_str); | ||
519 | } | ||
520 | sun4v_mach_sir(); | ||
521 | } | ||
522 | |||
364 | static void ds_conn_reset(struct ds_info *dp) | 523 | static void ds_conn_reset(struct ds_info *dp) |
365 | { | 524 | { |
366 | printk(KERN_ERR PFX "ds_conn_reset() from %p\n", | 525 | printk(KERN_ERR PFX "ds_conn_reset() from %p\n", |
@@ -594,6 +753,8 @@ static int __devinit ds_probe(struct vio_dev *vdev, | |||
594 | if (err) | 753 | if (err) |
595 | goto out_free_ldc; | 754 | goto out_free_ldc; |
596 | 755 | ||
756 | ds_info = dp; | ||
757 | |||
597 | start_powerd(); | 758 | start_powerd(); |
598 | 759 | ||
599 | return err; | 760 | return err; |