[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>
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c
index 9c8839d..4e20ef2 100644
--- a/arch/sparc64/kernel/ds.c
+++ b/arch/sparc64/kernel/ds.c
@@ -11,6 +11,7 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
+#include <linux/mutex.h>
 
 #include <asm/ldc.h>
 #include <asm/vio.h>
@@ -171,7 +172,7 @@
 
 	rp = (struct ds_md_update_req *) (dpkt + 1);
 
-	printk(KERN_ERR PFX "Machine description update.\n");
+	printk(KERN_INFO PFX "Machine description update.\n");
 
 	memset(&pkt, 0, sizeof(pkt));
 	pkt.data.tag.type = DS_DATA;
@@ -248,8 +249,8 @@
 
 	rp = (struct ds_panic_req *) (dpkt + 1);
 
-	printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n",
-	       rp->req_num, len);
+	printk(KERN_ALERT PFX "Panic request from "
+	       "LDOM manager received.\n");
 
 	memset(&pkt, 0, sizeof(pkt));
 	pkt.data.tag.type = DS_DATA;
@@ -313,10 +314,60 @@
 
 	rp = (struct ds_pri_msg *) (dpkt + 1);
 
-	printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n",
+	printk(KERN_INFO PFX "PRI REQ [%lx:%lx], len=%d\n",
 	       rp->req_num, rp->type, len);
 }
 
+struct ds_var_hdr {
+	__u32				type;
+#define DS_VAR_SET_REQ			0x00
+#define DS_VAR_DELETE_REQ		0x01
+#define DS_VAR_SET_RESP			0x02
+#define DS_VAR_DELETE_RESP		0x03
+};
+
+struct ds_var_set_msg {
+	struct ds_var_hdr		hdr;
+	char				name_and_value[0];
+};
+
+struct ds_var_delete_msg {
+	struct ds_var_hdr		hdr;
+	char				name[0];
+};
+
+struct ds_var_resp {
+	struct ds_var_hdr		hdr;
+	__u32				result;
+#define DS_VAR_SUCCESS			0x00
+#define DS_VAR_NO_SPACE			0x01
+#define DS_VAR_INVALID_VAR		0x02
+#define DS_VAR_INVALID_VAL		0x03
+#define DS_VAR_NOT_PRESENT		0x04
+};
+
+static DEFINE_MUTEX(ds_var_mutex);
+static int ds_var_doorbell;
+static int ds_var_response;
+
+static void ds_var_data(struct ldc_channel *lp,
+			struct ds_cap_state *dp,
+			void *buf, int len)
+{
+	struct ds_data *dpkt = buf;
+	struct ds_var_resp *rp;
+
+	rp = (struct ds_var_resp *) (dpkt + 1);
+
+	if (rp->hdr.type != DS_VAR_SET_RESP &&
+	    rp->hdr.type != DS_VAR_DELETE_RESP)
+		return;
+
+	ds_var_response = rp->result;
+	wmb();
+	ds_var_doorbell = 1;
+}
+
 struct ds_cap_state ds_states[] = {
 	{
 		.service_id	= "md-update",
@@ -338,17 +389,16 @@
 		.service_id	= "pri",
 		.data		= ds_pri_data,
 	},
+	{
+		.service_id	= "var-config",
+		.data		= ds_var_data,
+	},
+	{
+		.service_id	= "var-config-backup",
+		.data		= ds_var_data,
+	},
 };
 
-static struct ds_cap_state *find_cap(u64 handle)
-{
-	unsigned int index = handle >> 32;
-
-	if (index >= ARRAY_SIZE(ds_states))
-		return NULL;
-	return &ds_states[index];
-}
-
 static DEFINE_SPINLOCK(ds_lock);
 
 struct ds_info {
@@ -361,6 +411,115 @@
 	int			rcv_buf_len;
 };
 
+static struct ds_info *ds_info;
+
+static struct ds_cap_state *find_cap(u64 handle)
+{
+	unsigned int index = handle >> 32;
+
+	if (index >= ARRAY_SIZE(ds_states))
+		return NULL;
+	return &ds_states[index];
+}
+
+static struct ds_cap_state *find_cap_by_string(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ds_states); i++) {
+		if (strcmp(ds_states[i].service_id, name))
+			continue;
+
+		return &ds_states[i];
+	}
+	return NULL;
+}
+
+void ldom_set_var(const char *var, const char *value)
+{
+	struct ds_info *dp = ds_info;
+	struct ds_cap_state *cp;
+
+	cp = find_cap_by_string("var-config");
+	if (cp->state != CAP_STATE_REGISTERED)
+		cp = find_cap_by_string("var-config-backup");
+
+	if (cp->state == CAP_STATE_REGISTERED) {
+		union {
+			struct {
+				struct ds_data		data;
+				struct ds_var_set_msg	msg;
+			} header;
+			char			all[512];
+		} pkt;
+		unsigned long flags;
+		char  *base, *p;
+		int msg_len, loops;
+
+		memset(&pkt, 0, sizeof(pkt));
+		pkt.header.data.tag.type = DS_DATA;
+		pkt.header.data.handle = cp->handle;
+		pkt.header.msg.hdr.type = DS_VAR_SET_REQ;
+		base = p = &pkt.header.msg.name_and_value[0];
+		strcpy(p, var);
+		p += strlen(var) + 1;
+		strcpy(p, value);
+		p += strlen(value) + 1;
+
+		msg_len = (sizeof(struct ds_data) +
+			       sizeof(struct ds_var_set_msg) +
+			       (p - base));
+		msg_len = (msg_len + 3) & ~3;
+		pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag);
+
+		mutex_lock(&ds_var_mutex);
+
+		spin_lock_irqsave(&ds_lock, flags);
+		ds_var_doorbell = 0;
+		ds_var_response = -1;
+
+		ds_send(dp->lp, &pkt, msg_len);
+		spin_unlock_irqrestore(&ds_lock, flags);
+
+		loops = 1000;
+		while (ds_var_doorbell == 0) {
+			if (loops-- < 0)
+				break;
+			barrier();
+			udelay(100);
+		}
+
+		mutex_unlock(&ds_var_mutex);
+
+		if (ds_var_doorbell == 0 ||
+		    ds_var_response != DS_VAR_SUCCESS)
+			printk(KERN_ERR PFX "var-config [%s:%s] "
+			       "failed, response(%d).\n",
+			       var, value,
+			       ds_var_response);
+	} else {
+		printk(KERN_ERR PFX "var-config not registered so "
+		       "could not set (%s) variable to (%s).\n",
+		       var, value);
+	}
+}
+
+void ldom_reboot(const char *boot_command)
+{
+	/* Don't bother with any of this if the boot_command
+	 * is empty.
+	 */
+	if (boot_command && strlen(boot_command)) {
+		char full_boot_str[256];
+
+		strcpy(full_boot_str, "boot ");
+		strcpy(full_boot_str + strlen("boot "), boot_command);
+
+		ldom_set_var("reboot-command", full_boot_str);
+	}
+	sun4v_mach_sir();
+}
+
 static void ds_conn_reset(struct ds_info *dp)
 {
 	printk(KERN_ERR PFX "ds_conn_reset() from %p\n",
@@ -594,6 +753,8 @@
 	if (err)
 		goto out_free_ldc;
 
+	ds_info = dp;
+
 	start_powerd();
 
 	return err;
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c
index f3e0c14..72d272c 100644
--- a/arch/sparc64/prom/misc.c
+++ b/arch/sparc64/prom/misc.c
@@ -14,6 +14,7 @@
 #include <asm/openprom.h>
 #include <asm/oplib.h>
 #include <asm/system.h>
+#include <asm/ldc.h>
 
 int prom_service_exists(const char *service_name)
 {
@@ -37,6 +38,10 @@
 /* Reset and reboot the machine with the command 'bcommand'. */
 void prom_reboot(const char *bcommand)
 {
+#ifdef CONFIG_SUN_LDOMS
+	if (ldom_domaining_enabled)
+		ldom_reboot(bcommand);
+#endif
 	p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) |
 		  P1275_INOUT(1, 0), bcommand);
 }
diff --git a/arch/sparc64/prom/tree.c b/arch/sparc64/prom/tree.c
index 500f05e..17b7ecf 100644
--- a/arch/sparc64/prom/tree.c
+++ b/arch/sparc64/prom/tree.c
@@ -13,6 +13,7 @@
 
 #include <asm/openprom.h>
 #include <asm/oplib.h>
+#include <asm/ldc.h>
 
 /* Return the child of node 'node' or zero if no this node has no
  * direct descendent.
@@ -261,9 +262,17 @@
 int
 prom_setprop(int node, const char *pname, char *value, int size)
 {
-	if(size == 0) return 0;
-	if((pname == 0) || (value == 0)) return 0;
+	if (size == 0)
+		return 0;
+	if ((pname == 0) || (value == 0))
+		return 0;
 	
+#ifdef CONFIG_SUN_LDOMS
+	if (ldom_domaining_enabled) {
+		ldom_set_var(pname, value);
+		return 0;
+	}
+#endif
 	return p1275_cmd ("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
 					  P1275_ARG(2,P1275_ARG_IN_BUF)|
 					  P1275_INOUT(4, 1), 
diff --git a/include/asm-sparc64/ldc.h b/include/asm-sparc64/ldc.h
index 3c91f26..a21996c 100644
--- a/include/asm-sparc64/ldc.h
+++ b/include/asm-sparc64/ldc.h
@@ -4,6 +4,8 @@
 #include <asm/hypervisor.h>
 
 extern int ldom_domaining_enabled;
+extern void ldom_set_var(const char *var, const char *value);
+extern void ldom_reboot(const char *boot_command);
 
 /* The event handler will be evoked when link state changes
  * or data becomes available on the receive side.