summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sparc64/Kconfig10
-rw-r--r--arch/sparc64/kernel/Makefile3
-rw-r--r--arch/sparc64/kernel/ds.c514
-rw-r--r--arch/sparc64/kernel/hvtramp.S139
-rw-r--r--arch/sparc64/kernel/mdesc.c53
-rw-r--r--arch/sparc64/kernel/prom.c2
-rw-r--r--arch/sparc64/kernel/smp.c55
-rw-r--r--arch/sparc64/kernel/sparc64_ksyms.c4
-rw-r--r--arch/sparc64/prom/misc.c8
-rw-r--r--arch/sparc64/prom/p1275.c1
-rw-r--r--include/asm-sparc64/cpudata.h3
-rw-r--r--include/asm-sparc64/hvtramp.h37
-rw-r--r--include/asm-sparc64/hypervisor.h2
-rw-r--r--include/asm-sparc64/ldc.h2
-rw-r--r--include/asm-sparc64/mdesc.h3
-rw-r--r--include/asm-sparc64/smp.h8
16 files changed, 716 insertions, 128 deletions
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index af59daa81058..3c2e3397caf8 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -108,6 +108,15 @@ config SECCOMP
source kernel/Kconfig.hz
+config HOTPLUG_CPU
+ bool "Support for hot-pluggable CPUs"
+ depends on SMP
+ select HOTPLUG
+ ---help---
+ Say Y here to experiment with turning CPUs off and on. CPUs
+ can be controlled through /sys/devices/system/cpu/cpu#.
+ Say N if you want to disable CPU hotplug.
+
source "init/Kconfig"
config SYSVIPC_COMPAT
@@ -307,6 +316,7 @@ config SUN_IO
config SUN_LDOMS
bool "Sun Logical Domains support"
+ select HOTPLUG_CPU
help
Say Y here is you want to support virtual devices via
Logical Domains.
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 70e6c501392a..62db93c148cd 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -12,7 +12,8 @@ obj-y := process.o setup.o cpu.o idprom.o \
irq.o ptrace.o time.o sys_sparc.o signal.o \
unaligned.o central.o pci.o starfire.o semaphore.o \
power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
- visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o
+ visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o \
+ hvtramp.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c
index 4e20ef232c51..b82c03a25d9c 100644
--- a/arch/sparc64/kernel/ds.c
+++ b/arch/sparc64/kernel/ds.c
@@ -12,11 +12,16 @@
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/cpu.h>
#include <asm/ldc.h>
#include <asm/vio.h>
#include <asm/power.h>
#include <asm/mdesc.h>
+#include <asm/head.h>
+#include <asm/io.h>
+#include <asm/hvtramp.h>
#define DRV_MODULE_NAME "ds"
#define PFX DRV_MODULE_NAME ": "
@@ -124,7 +129,7 @@ struct ds_cap_state {
__u64 handle;
void (*data)(struct ldc_channel *lp,
- struct ds_cap_state *dp,
+ struct ds_cap_state *cp,
void *buf, int len);
const char *service_id;
@@ -135,6 +140,91 @@ struct ds_cap_state {
#define CAP_STATE_REGISTERED 0x02
};
+static void md_update_data(struct ldc_channel *lp, struct ds_cap_state *cp,
+ void *buf, int len);
+static void domain_shutdown_data(struct ldc_channel *lp,
+ struct ds_cap_state *cp,
+ void *buf, int len);
+static void domain_panic_data(struct ldc_channel *lp,
+ struct ds_cap_state *cp,
+ void *buf, int len);
+static void dr_cpu_data(struct ldc_channel *lp,
+ struct ds_cap_state *cp,
+ void *buf, int len);
+static void ds_pri_data(struct ldc_channel *lp,
+ struct ds_cap_state *cp,
+ void *buf, int len);
+static void ds_var_data(struct ldc_channel *lp,
+ struct ds_cap_state *cp,
+ void *buf, int len);
+
+struct ds_cap_state ds_states[] = {
+ {
+ .service_id = "md-update",
+ .data = md_update_data,
+ },
+ {
+ .service_id = "domain-shutdown",
+ .data = domain_shutdown_data,
+ },
+ {
+ .service_id = "domain-panic",
+ .data = domain_panic_data,
+ },
+ {
+ .service_id = "dr-cpu",
+ .data = dr_cpu_data,
+ },
+ {
+ .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 DEFINE_SPINLOCK(ds_lock);
+
+struct ds_info {
+ struct ldc_channel *lp;
+ u8 hs_state;
+#define DS_HS_START 0x01
+#define DS_HS_DONE 0x02
+
+ void *rcv_buf;
+ 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;
+}
+
static int ds_send(struct ldc_channel *lp, void *data, int len)
{
int err, limit = 1000;
@@ -265,36 +355,354 @@ static void domain_panic_data(struct ldc_channel *lp,
panic("PANIC requested by LDOM manager.");
}
-struct ds_cpu_tag {
+struct dr_cpu_tag {
__u64 req_num;
__u32 type;
-#define DS_CPU_CONFIGURE 0x43
-#define DS_CPU_UNCONFIGURE 0x55
-#define DS_CPU_FORCE_UNCONFIGURE 0x46
-#define DS_CPU_STATUS 0x53
+#define DR_CPU_CONFIGURE 0x43
+#define DR_CPU_UNCONFIGURE 0x55
+#define DR_CPU_FORCE_UNCONFIGURE 0x46
+#define DR_CPU_STATUS 0x53
/* Responses */
-#define DS_CPU_OK 0x6f
-#define DS_CPU_ERROR 0x65
+#define DR_CPU_OK 0x6f
+#define DR_CPU_ERROR 0x65
__u32 num_records;
};
-struct ds_cpu_record {
- __u32 cpu_id;
+struct dr_cpu_resp_entry {
+ __u32 cpu;
+ __u32 result;
+#define DR_CPU_RES_OK 0x00
+#define DR_CPU_RES_FAILURE 0x01
+#define DR_CPU_RES_BLOCKED 0x02
+#define DR_CPU_RES_CPU_NOT_RESPONDING 0x03
+#define DR_CPU_RES_NOT_IN_MD 0x04
+
+ __u32 stat;
+#define DR_CPU_STAT_NOT_PRESENT 0x00
+#define DR_CPU_STAT_UNCONFIGURED 0x01
+#define DR_CPU_STAT_CONFIGURED 0x02
+
+ __u32 str_off;
};
+/* XXX Put this in some common place. XXX */
+static unsigned long kimage_addr_to_ra(void *p)
+{
+ unsigned long val = (unsigned long) p;
+
+ return kern_base + (val - KERNBASE);
+}
+
+void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg)
+{
+ extern unsigned long sparc64_ttable_tl0;
+ extern unsigned long kern_locked_tte_data;
+ extern int bigkernel;
+ struct hvtramp_descr *hdesc;
+ unsigned long trampoline_ra;
+ struct trap_per_cpu *tb;
+ u64 tte_vaddr, tte_data;
+ unsigned long hv_err;
+
+ hdesc = kzalloc(sizeof(*hdesc), GFP_KERNEL);
+ if (!hdesc) {
+ printk(KERN_ERR PFX "ldom_startcpu_cpuid: Cannot allocate "
+ "hvtramp_descr.\n");
+ return;
+ }
+
+ hdesc->cpu = cpu;
+ hdesc->num_mappings = (bigkernel ? 2 : 1);
+
+ tb = &trap_block[cpu];
+ tb->hdesc = hdesc;
+
+ hdesc->fault_info_va = (unsigned long) &tb->fault_info;
+ hdesc->fault_info_pa = kimage_addr_to_ra(&tb->fault_info);
+
+ hdesc->thread_reg = thread_reg;
+
+ tte_vaddr = (unsigned long) KERNBASE;
+ tte_data = kern_locked_tte_data;
+
+ hdesc->maps[0].vaddr = tte_vaddr;
+ hdesc->maps[0].tte = tte_data;
+ if (bigkernel) {
+ tte_vaddr += 0x400000;
+ tte_data += 0x400000;
+ hdesc->maps[1].vaddr = tte_vaddr;
+ hdesc->maps[1].tte = tte_data;
+ }
+
+ trampoline_ra = kimage_addr_to_ra(hv_cpu_startup);
+
+ hv_err = sun4v_cpu_start(cpu, trampoline_ra,
+ kimage_addr_to_ra(&sparc64_ttable_tl0),
+ __pa(hdesc));
+}
+
+/* DR cpu requests get queued onto the work list by the
+ * dr_cpu_data() callback. The list is protected by
+ * ds_lock, and processed by dr_cpu_process() in order.
+ */
+static LIST_HEAD(dr_cpu_work_list);
+
+struct dr_cpu_queue_entry {
+ struct list_head list;
+ char req[0];
+};
+
+static void __dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data)
+{
+ struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1);
+ struct ds_info *dp = ds_info;
+ struct {
+ struct ds_data data;
+ struct dr_cpu_tag tag;
+ } pkt;
+ int msg_len;
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.data.tag.type = DS_DATA;
+ pkt.data.handle = cp->handle;
+ pkt.tag.req_num = tag->req_num;
+ pkt.tag.type = DR_CPU_ERROR;
+ pkt.tag.num_records = 0;
+
+ msg_len = (sizeof(struct ds_data) +
+ sizeof(struct dr_cpu_tag));
+
+ pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag);
+
+ ds_send(dp->lp, &pkt, msg_len);
+}
+
+static void dr_cpu_send_error(struct ds_cap_state *cp, struct ds_data *data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ds_lock, flags);
+ __dr_cpu_send_error(cp, data);
+ spin_unlock_irqrestore(&ds_lock, flags);
+}
+
+#define CPU_SENTINEL 0xffffffff
+
+static void purge_dups(u32 *list, u32 num_ents)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_ents; i++) {
+ u32 cpu = list[i];
+ unsigned int j;
+
+ if (cpu == CPU_SENTINEL)
+ continue;
+
+ for (j = i + 1; j < num_ents; j++) {
+ if (list[j] == cpu)
+ list[j] = CPU_SENTINEL;
+ }
+ }
+}
+
+static int dr_cpu_size_response(int ncpus)
+{
+ return (sizeof(struct ds_data) +
+ sizeof(struct dr_cpu_tag) +
+ (sizeof(struct dr_cpu_resp_entry) * ncpus));
+}
+
+static void dr_cpu_init_response(struct ds_data *resp, u64 req_num,
+ u64 handle, int resp_len, int ncpus,
+ cpumask_t *mask, u32 default_stat)
+{
+ struct dr_cpu_resp_entry *ent;
+ struct dr_cpu_tag *tag;
+ int i, cpu;
+
+ tag = (struct dr_cpu_tag *) (resp + 1);
+ ent = (struct dr_cpu_resp_entry *) (tag + 1);
+
+ resp->tag.type = DS_DATA;
+ resp->tag.len = resp_len - sizeof(struct ds_msg_tag);
+ resp->handle = handle;
+ tag->req_num = req_num;
+ tag->type = DR_CPU_OK;
+ tag->num_records = ncpus;
+
+ i = 0;
+ for_each_cpu_mask(cpu, *mask) {
+ ent[i].cpu = cpu;
+ ent[i].result = DR_CPU_RES_OK;
+ ent[i].stat = default_stat;
+ i++;
+ }
+ BUG_ON(i != ncpus);
+}
+
+static void dr_cpu_mark(struct ds_data *resp, int cpu, int ncpus,
+ u32 res, u32 stat)
+{
+ struct dr_cpu_resp_entry *ent;
+ struct dr_cpu_tag *tag;
+ int i;
+
+ tag = (struct dr_cpu_tag *) (resp + 1);
+ ent = (struct dr_cpu_resp_entry *) (tag + 1);
+
+ for (i = 0; i < ncpus; i++) {
+ if (ent[i].cpu != cpu)
+ continue;
+ ent[i].result = res;
+ ent[i].stat = stat;
+ break;
+ }
+}
+
+static int dr_cpu_configure(struct ds_cap_state *cp, u64 req_num,
+ cpumask_t *mask)
+{
+ struct ds_data *resp;
+ int resp_len, ncpus, cpu;
+ unsigned long flags;
+
+ ncpus = cpus_weight(*mask);
+ resp_len = dr_cpu_size_response(ncpus);
+ resp = kzalloc(resp_len, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ dr_cpu_init_response(resp, req_num, cp->handle,
+ resp_len, ncpus, mask,
+ DR_CPU_STAT_CONFIGURED);
+
+ mdesc_fill_in_cpu_data(*mask);
+
+ for_each_cpu_mask(cpu, *mask) {
+ int err;
+
+ printk(KERN_INFO PFX "Starting cpu %d...\n", cpu);
+ err = cpu_up(cpu);
+ if (err)
+ dr_cpu_mark(resp, cpu, ncpus,
+ DR_CPU_RES_FAILURE,
+ DR_CPU_STAT_UNCONFIGURED);
+ }
+
+ spin_lock_irqsave(&ds_lock, flags);
+ ds_send(ds_info->lp, resp, resp_len);
+ spin_unlock_irqrestore(&ds_lock, flags);
+
+ kfree(resp);
+
+ return 0;
+}
+
+static int dr_cpu_unconfigure(struct ds_cap_state *cp, u64 req_num,
+ cpumask_t *mask)
+{
+ struct ds_data *resp;
+ int resp_len, ncpus;
+
+ ncpus = cpus_weight(*mask);
+ resp_len = dr_cpu_size_response(ncpus);
+ resp = kzalloc(resp_len, GFP_KERNEL);
+ if (!resp)
+ return -ENOMEM;
+
+ dr_cpu_init_response(resp, req_num, cp->handle,
+ resp_len, ncpus, mask,
+ DR_CPU_STAT_UNCONFIGURED);
+
+ kfree(resp);
+
+ return -EOPNOTSUPP;
+}
+
+static void dr_cpu_process(struct work_struct *work)
+{
+ struct dr_cpu_queue_entry *qp, *tmp;
+ struct ds_cap_state *cp;
+ unsigned long flags;
+ LIST_HEAD(todo);
+ cpumask_t mask;
+
+ cp = find_cap_by_string("dr-cpu");
+
+ spin_lock_irqsave(&ds_lock, flags);
+ list_splice(&dr_cpu_work_list, &todo);
+ spin_unlock_irqrestore(&ds_lock, flags);
+
+ list_for_each_entry_safe(qp, tmp, &todo, list) {
+ struct ds_data *data = (struct ds_data *) qp->req;
+ struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1);
+ u32 *cpu_list = (u32 *) (tag + 1);
+ u64 req_num = tag->req_num;
+ unsigned int i;
+ int err;
+
+ switch (tag->type) {
+ case DR_CPU_CONFIGURE:
+ case DR_CPU_UNCONFIGURE:
+ case DR_CPU_FORCE_UNCONFIGURE:
+ break;
+
+ default:
+ dr_cpu_send_error(cp, data);
+ goto next;
+ }
+
+ purge_dups(cpu_list, tag->num_records);
+
+ cpus_clear(mask);
+ for (i = 0; i < tag->num_records; i++) {
+ if (cpu_list[i] == CPU_SENTINEL)
+ continue;
+
+ if (cpu_list[i] < NR_CPUS)
+ cpu_set(cpu_list[i], mask);
+ }
+
+ if (tag->type == DR_CPU_CONFIGURE)
+ err = dr_cpu_configure(cp, req_num, &mask);
+ else
+ err = dr_cpu_unconfigure(cp, req_num, &mask);
+
+ if (err)
+ dr_cpu_send_error(cp, data);
+
+next:
+ list_del(&qp->list);
+ kfree(qp);
+ }
+}
+
+static DECLARE_WORK(dr_cpu_work, dr_cpu_process);
+
static void dr_cpu_data(struct ldc_channel *lp,
struct ds_cap_state *dp,
void *buf, int len)
{
+ struct dr_cpu_queue_entry *qp;
struct ds_data *dpkt = buf;
- struct ds_cpu_tag *rp;
+ struct dr_cpu_tag *rp;
- rp = (struct ds_cpu_tag *) (dpkt + 1);
+ rp = (struct dr_cpu_tag *) (dpkt + 1);
- printk(KERN_ERR PFX "CPU REQ [%lx:%x], len=%d\n",
- rp->req_num, rp->type, len);
+ qp = kmalloc(sizeof(struct dr_cpu_queue_entry) + len, GFP_ATOMIC);
+ if (!qp) {
+ struct ds_cap_state *cp;
+
+ cp = find_cap_by_string("dr-cpu");
+ __dr_cpu_send_error(cp, dpkt);
+ } else {
+ memcpy(&qp->req, buf, len);
+ list_add_tail(&qp->list, &dr_cpu_work_list);
+ schedule_work(&dr_cpu_work);
+ }
}
struct ds_pri_msg {
@@ -368,73 +776,6 @@ static void ds_var_data(struct ldc_channel *lp,
ds_var_doorbell = 1;
}
-struct ds_cap_state ds_states[] = {
- {
- .service_id = "md-update",
- .data = md_update_data,
- },
- {
- .service_id = "domain-shutdown",
- .data = domain_shutdown_data,
- },
- {
- .service_id = "domain-panic",
- .data = domain_panic_data,
- },
- {
- .service_id = "dr-cpu",
- .data = dr_cpu_data,
- },
- {
- .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 DEFINE_SPINLOCK(ds_lock);
-
-struct ds_info {
- struct ldc_channel *lp;
- u8 hs_state;
-#define DS_HS_START 0x01
-#define DS_HS_DONE 0x02
-
- void *rcv_buf;
- 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;
@@ -467,8 +808,8 @@ void ldom_set_var(const char *var, const char *value)
p += strlen(value) + 1;
msg_len = (sizeof(struct ds_data) +
- sizeof(struct ds_var_set_msg) +
- (p - base));
+ 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);
@@ -520,6 +861,11 @@ void ldom_reboot(const char *boot_command)
sun4v_mach_sir();
}
+void ldom_power_off(void)
+{
+ sun4v_mach_exit(0);
+}
+
static void ds_conn_reset(struct ds_info *dp)
{
printk(KERN_ERR PFX "ds_conn_reset() from %p\n",
@@ -601,7 +947,7 @@ static int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt)
np->handle);
return 0;
}
- printk(KERN_ERR PFX "Could not register %s service\n",
+ printk(KERN_INFO PFX "Could not register %s service\n",
cp->service_id);
cp->state = CAP_STATE_UNKNOWN;
}
diff --git a/arch/sparc64/kernel/hvtramp.S b/arch/sparc64/kernel/hvtramp.S
new file mode 100644
index 000000000000..76a090e2c2a8
--- /dev/null
+++ b/arch/sparc64/kernel/hvtramp.S
@@ -0,0 +1,139 @@
+/* hvtramp.S: Hypervisor start-cpu trampoline code.
+ *
+ * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ */
+
+#include <asm/thread_info.h>
+#include <asm/hypervisor.h>
+#include <asm/scratchpad.h>
+#include <asm/spitfire.h>
+#include <asm/hvtramp.h>
+#include <asm/pstate.h>
+#include <asm/ptrace.h>
+#include <asm/asi.h>
+
+ .text
+ .align 8
+ .globl hv_cpu_startup, hv_cpu_startup_end
+
+ /* This code executes directly out of the hypervisor
+ * with physical addressing (va==pa). %o0 contains
+ * our client argument which for Linux points to
+ * a descriptor data structure which defines the
+ * MMU entries we need to load up.
+ *
+ * After we set things up we enable the MMU and call
+ * into the kernel.
+ *
+ * First setup basic privileged cpu state.
+ */
+hv_cpu_startup:
+ wrpr %g0, 0, %gl
+ wrpr %g0, 15, %pil
+ wrpr %g0, 0, %canrestore
+ wrpr %g0, 0, %otherwin
+ wrpr %g0, 6, %cansave
+ wrpr %g0, 6, %cleanwin
+ wrpr %g0, 0, %cwp
+ wrpr %g0, 0, %wstate
+ wrpr %g0, 0, %tl
+
+ sethi %hi(sparc64_ttable_tl0), %g1
+ wrpr %g1, %tba
+
+ mov %o0, %l0
+
+ lduw [%l0 + HVTRAMP_DESCR_CPU], %g1
+ mov SCRATCHPAD_CPUID, %g2
+ stxa %g1, [%g2] ASI_SCRATCHPAD
+
+ ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_VA], %g2
+ stxa %g2, [%g0] ASI_SCRATCHPAD
+
+ mov 0, %l1
+ lduw [%l0 + HVTRAMP_DESCR_NUM_MAPPINGS], %l2
+ add %l0, HVTRAMP_DESCR_MAPS, %l3
+
+1: ldx [%l3 + HVTRAMP_MAPPING_VADDR], %o0
+ clr %o1
+ ldx [%l3 + HVTRAMP_MAPPING_TTE], %o2
+ mov HV_MMU_IMMU | HV_MMU_DMMU, %o3
+ mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
+ ta HV_FAST_TRAP
+
+ brnz,pn %o0, 80f
+ nop
+
+ add %l1, 1, %l1
+ cmp %l1, %l2
+ blt,a,pt %xcc, 1b
+ add %l3, HVTRAMP_MAPPING_SIZE, %l3
+
+ ldx [%l0 + HVTRAMP_DESCR_FAULT_INFO_PA], %o0
+ mov HV_FAST_MMU_FAULT_AREA_CONF, %o5
+ ta HV_FAST_TRAP
+
+ brnz,pn %o0, 80f
+ nop
+
+ wrpr %g0, (PSTATE_PRIV | PSTATE_PEF), %pstate
+
+ ldx [%l0 + HVTRAMP_DESCR_THREAD_REG], %l6
+
+ mov 1, %o0
+ set 1f, %o1
+ mov HV_FAST_MMU_ENABLE, %o5
+ ta HV_FAST_TRAP
+
+ ba,pt %xcc, 80f
+ nop
+
+1:
+ wr %g0, 0, %fprs
+ wr %g0, ASI_P, %asi
+
+ mov PRIMARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_MMU
+ membar #Sync
+
+ mov SECONDARY_CONTEXT, %g7
+ stxa %g0, [%g7] ASI_MMU
+ membar #Sync
+
+ mov %l6, %g6
+ ldx [%g6 + TI_TASK], %g4
+
+ mov 1, %g5
+ sllx %g5, THREAD_SHIFT, %g5
+ sub %g5, (STACKFRAME_SZ + STACK_BIAS), %g5
+ add %g6, %g5, %sp
+ mov 0, %fp
+
+ call init_irqwork_curcpu
+ nop
+ call hard_smp_processor_id
+ nop
+
+ mov %o0, %o1
+ mov 0, %o0
+ mov 0, %o2
+ call sun4v_init_mondo_queues
+ mov 1, %o3
+
+ call init_cur_cpu_trap
+ mov %g6, %o0
+
+ wrpr %g0, (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE), %pstate
+
+ call smp_callin
+ nop
+ call cpu_idle
+ mov 0, %o0
+ call cpu_panic
+ nop
+
+80: ba,pt %xcc, 80b
+ nop
+
+ .align 8
+hv_cpu_startup_end:
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c
index 9e5088d563cc..3f79940a2939 100644
--- a/arch/sparc64/kernel/mdesc.c
+++ b/arch/sparc64/kernel/mdesc.c
@@ -434,6 +434,22 @@ static void __init report_platform_properties(void)
if (v)
printk("PLATFORM: max-cpus [%lu]\n", *v);
+#ifdef CONFIG_SMP
+ {
+ int max_cpu, i;
+
+ if (v) {
+ max_cpu = *v;
+ if (max_cpu > NR_CPUS)
+ max_cpu = NR_CPUS;
+ } else {
+ max_cpu = NR_CPUS;
+ }
+ for (i = 0; i < max_cpu; i++)
+ cpu_set(i, cpu_possible_map);
+ }
+#endif
+
mdesc_release(hp);
}
@@ -451,9 +467,9 @@ static int inline find_in_proplist(const char *list, const char *match, int len)
return 0;
}
-static void __init fill_in_one_cache(cpuinfo_sparc *c,
- struct mdesc_handle *hp,
- u64 mp)
+static void __devinit fill_in_one_cache(cpuinfo_sparc *c,
+ struct mdesc_handle *hp,
+ u64 mp)
{
const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
@@ -496,7 +512,8 @@ static void __init fill_in_one_cache(cpuinfo_sparc *c,
}
}
-static void __init mark_core_ids(struct mdesc_handle *hp, u64 mp, int core_id)
+static void __devinit mark_core_ids(struct mdesc_handle *hp, u64 mp,
+ int core_id)
{
u64 a;
@@ -529,7 +546,7 @@ static void __init mark_core_ids(struct mdesc_handle *hp, u64 mp, int core_id)
}
}
-static void __init set_core_ids(struct mdesc_handle *hp)
+static void __devinit set_core_ids(struct mdesc_handle *hp)
{
int idx;
u64 mp;
@@ -554,7 +571,8 @@ static void __init set_core_ids(struct mdesc_handle *hp)
}
}
-static void __init mark_proc_ids(struct mdesc_handle *hp, u64 mp, int proc_id)
+static void __devinit mark_proc_ids(struct mdesc_handle *hp, u64 mp,
+ int proc_id)
{
u64 a;
@@ -573,8 +591,8 @@ static void __init mark_proc_ids(struct mdesc_handle *hp, u64 mp, int proc_id)
}
}
-static void __init __set_proc_ids(struct mdesc_handle *hp,
- const char *exec_unit_name)
+static void __devinit __set_proc_ids(struct mdesc_handle *hp,
+ const char *exec_unit_name)
{
int idx;
u64 mp;
@@ -595,13 +613,14 @@ static void __init __set_proc_ids(struct mdesc_handle *hp,
}
}
-static void __init set_proc_ids(struct mdesc_handle *hp)
+static void __devinit set_proc_ids(struct mdesc_handle *hp)
{
__set_proc_ids(hp, "exec_unit");
__set_proc_ids(hp, "exec-unit");
}
-static void __init get_one_mondo_bits(const u64 *p, unsigned int *mask, unsigned char def)
+static void __devinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
+ unsigned char def)
{
u64 val;
@@ -619,8 +638,8 @@ use_default:
*mask = ((1U << def) * 64U) - 1U;
}
-static void __init get_mondo_data(struct mdesc_handle *hp, u64 mp,
- struct trap_per_cpu *tb)
+static void __devinit get_mondo_data(struct mdesc_handle *hp, u64 mp,
+ struct trap_per_cpu *tb)
{
const u64 *val;
@@ -637,7 +656,7 @@ static void __init get_mondo_data(struct mdesc_handle *hp, u64 mp,
get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
}
-static void __init mdesc_fill_in_cpu_data(void)
+void __devinit mdesc_fill_in_cpu_data(cpumask_t mask)
{
struct mdesc_handle *hp = mdesc_grab();
u64 mp;
@@ -658,6 +677,8 @@ static void __init mdesc_fill_in_cpu_data(void)
#ifdef CONFIG_SMP
if (cpuid >= NR_CPUS)
continue;
+ if (!cpu_isset(cpuid, mask))
+ continue;
#else
/* On uniprocessor we only want the values for the
* real physical cpu the kernel booted onto, however
@@ -696,7 +717,6 @@ static void __init mdesc_fill_in_cpu_data(void)
#ifdef CONFIG_SMP
cpu_set(cpuid, cpu_present_map);
- cpu_set(cpuid, phys_cpu_present_map);
#endif
c->core_id = 0;
@@ -719,6 +739,7 @@ void __init sun4v_mdesc_init(void)
{
struct mdesc_handle *hp;
unsigned long len, real_len, status;
+ cpumask_t mask;
(void) sun4v_mach_desc(0UL, 0UL, &len);
@@ -742,5 +763,7 @@ void __init sun4v_mdesc_init(void)
cur_mdesc = hp;
report_platform_properties();
- mdesc_fill_in_cpu_data();
+
+ cpus_setall(mask);
+ mdesc_fill_in_cpu_data(mask);
}
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 61036b346664..5d220302cd50 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -1808,7 +1808,7 @@ static void __init of_fill_in_cpu_data(void)
#ifdef CONFIG_SMP
cpu_set(cpuid, cpu_present_map);
- cpu_set(cpuid, phys_cpu_present_map);
+ cpu_set(cpuid, cpu_possible_map);
#endif
}
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 40e40f968d61..315eef0869bd 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -41,6 +41,7 @@
#include <asm/sections.h>
#include <asm/prom.h>
#include <asm/mdesc.h>
+#include <asm/ldc.h>
extern void calibrate_delay(void);
@@ -49,12 +50,18 @@ int sparc64_multi_core __read_mostly;
/* Please don't make this stuff initdata!!! --DaveM */
unsigned char boot_cpu_id;
+cpumask_t cpu_possible_map __read_mostly = CPU_MASK_NONE;
cpumask_t cpu_online_map __read_mostly = CPU_MASK_NONE;
-cpumask_t phys_cpu_present_map __read_mostly = CPU_MASK_NONE;
cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly =
{ [0 ... NR_CPUS-1] = CPU_MASK_NONE };
cpumask_t cpu_core_map[NR_CPUS] __read_mostly =
{ [0 ... NR_CPUS-1] = CPU_MASK_NONE };
+
+EXPORT_SYMBOL(cpu_possible_map);
+EXPORT_SYMBOL(cpu_online_map);
+EXPORT_SYMBOL(cpu_sibling_map);
+EXPORT_SYMBOL(cpu_core_map);
+
static cpumask_t smp_commenced_mask;
static cpumask_t cpu_callout_map;
@@ -84,9 +91,10 @@ extern void setup_sparc64_timer(void);
static volatile unsigned long callin_flag = 0;
-void __init smp_callin(void)
+void __devinit smp_callin(void)
{
int cpuid = hard_smp_processor_id();
+ struct trap_per_cpu *tb = &trap_block[cpuid];;
__local_per_cpu_offset = __per_cpu_offset(cpuid);
@@ -117,6 +125,11 @@ void __init smp_callin(void)
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
+ if (tb->hdesc) {
+ kfree(tb->hdesc);
+ tb->hdesc = NULL;
+ }
+
while (!cpu_isset(cpuid, smp_commenced_mask))
rmb();
@@ -296,14 +309,20 @@ static int __devinit smp_boot_one_cpu(unsigned int cpu)
/* Alloc the mondo queues, cpu will load them. */
sun4v_init_mondo_queues(0, cpu, 1, 0);
- prom_startcpu_cpuid(cpu, entry, cookie);
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled)
+ ldom_startcpu_cpuid(cpu,
+ (unsigned long) cpu_new_thread);
+ else
+#endif
+ prom_startcpu_cpuid(cpu, entry, cookie);
} else {
struct device_node *dp = of_find_node_by_cpuid(cpu);
prom_startcpu(dp->node, entry, cookie);
}
- for (timeout = 0; timeout < 5000000; timeout++) {
+ for (timeout = 0; timeout < 50000; timeout++) {
if (callin_flag)
break;
udelay(100);
@@ -1163,22 +1182,8 @@ int setup_profiling_timer(unsigned int multiplier)
return -EINVAL;
}
-/* Constrain the number of cpus to max_cpus. */
void __init smp_prepare_cpus(unsigned int max_cpus)
{
- int i;
-
- if (num_possible_cpus() > max_cpus) {
- for_each_possible_cpu(i) {
- if (i != boot_cpu_id) {
- cpu_clear(i, phys_cpu_present_map);
- cpu_clear(i, cpu_present_map);
- if (num_possible_cpus() <= max_cpus)
- break;
- }
- }
- }
-
cpu_data(boot_cpu_id).udelay_val = loops_per_jiffy;
}
@@ -1242,6 +1247,20 @@ int __cpuinit __cpu_up(unsigned int cpu)
return ret;
}
+#ifdef CONFIG_HOTPLUG_CPU
+int __cpu_disable(void)
+{
+ printk(KERN_ERR "SMP: __cpu_disable() on cpu %d\n",
+ smp_processor_id());
+ return -ENODEV;
+}
+
+void __cpu_die(unsigned int cpu)
+{
+ printk(KERN_ERR "SMP: __cpu_die(%u)\n", cpu);
+}
+#endif
+
void __init smp_cpus_done(unsigned int max_cpus)
{
unsigned long bogosum = 0;
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 6fa761612899..51e059e36d47 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -124,10 +124,6 @@ EXPORT_SYMBOL(__write_lock);
EXPORT_SYMBOL(__write_unlock);
EXPORT_SYMBOL(__write_trylock);
-/* CPU online map and active count. */
-EXPORT_SYMBOL(cpu_online_map);
-EXPORT_SYMBOL(phys_cpu_present_map);
-
EXPORT_SYMBOL(smp_call_function);
#endif /* CONFIG_SMP */
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c
index 72d272c9de6b..33c5b7da31e5 100644
--- a/arch/sparc64/prom/misc.c
+++ b/arch/sparc64/prom/misc.c
@@ -96,6 +96,10 @@ void prom_cmdline(void)
*/
void prom_halt(void)
{
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled)
+ ldom_power_off();
+#endif
again:
p1275_cmd("exit", P1275_INOUT(0, 0));
goto again; /* PROM is out to get me -DaveM */
@@ -103,6 +107,10 @@ again:
void prom_halt_power_off(void)
{
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled)
+ ldom_power_off();
+#endif
p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0));
/* if nothing else helps, we just halt */
diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c
index 2b32c489860c..7fcccc0e19cf 100644
--- a/arch/sparc64/prom/p1275.c
+++ b/arch/sparc64/prom/p1275.c
@@ -16,6 +16,7 @@
#include <asm/system.h>
#include <asm/spitfire.h>
#include <asm/pstate.h>
+#include <asm/ldc.h>
struct {
long prom_callback; /* 0x00 */
diff --git a/include/asm-sparc64/cpudata.h b/include/asm-sparc64/cpudata.h
index 445026fbec35..0016d8b4531c 100644
--- a/include/asm-sparc64/cpudata.h
+++ b/include/asm-sparc64/cpudata.h
@@ -80,7 +80,8 @@ struct trap_per_cpu {
unsigned int dev_mondo_qmask;
unsigned int resum_qmask;
unsigned int nonresum_qmask;
- unsigned int __pad2[3];
+ unsigned int __pad2[1];
+ void *hdesc;
} __attribute__((aligned(64)));
extern struct trap_per_cpu trap_block[NR_CPUS];
extern void init_cur_cpu_trap(struct thread_info *);
diff --git a/include/asm-sparc64/hvtramp.h b/include/asm-sparc64/hvtramp.h
new file mode 100644
index 000000000000..c7dd6ad056df
--- /dev/null
+++ b/include/asm-sparc64/hvtramp.h
@@ -0,0 +1,37 @@
+#ifndef _SPARC64_HVTRAP_H
+#define _SPARC64_HVTRAP_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+struct hvtramp_mapping {
+ __u64 vaddr;
+ __u64 tte;
+};
+
+struct hvtramp_descr {
+ __u32 cpu;
+ __u32 num_mappings;
+ __u64 fault_info_va;
+ __u64 fault_info_pa;
+ __u64 thread_reg;
+ struct hvtramp_mapping maps[2];
+};
+
+extern void hv_cpu_startup(unsigned long hvdescr_pa);
+
+#endif
+
+#define HVTRAMP_DESCR_CPU 0x00
+#define HVTRAMP_DESCR_NUM_MAPPINGS 0x04
+#define HVTRAMP_DESCR_FAULT_INFO_VA 0x08
+#define HVTRAMP_DESCR_FAULT_INFO_PA 0x10
+#define HVTRAMP_DESCR_THREAD_REG 0x18
+#define HVTRAMP_DESCR_MAPS 0x20
+
+#define HVTRAMP_MAPPING_VADDR 0x00
+#define HVTRAMP_MAPPING_TTE 0x08
+#define HVTRAMP_MAPPING_SIZE 0x10
+
+#endif /* _SPARC64_HVTRAP_H */
diff --git a/include/asm-sparc64/hypervisor.h b/include/asm-sparc64/hypervisor.h
index db2130a95d68..524d49835dfd 100644
--- a/include/asm-sparc64/hypervisor.h
+++ b/include/asm-sparc64/hypervisor.h
@@ -98,7 +98,7 @@
#define HV_FAST_MACH_EXIT 0x00
#ifndef __ASSEMBLY__
-extern void sun4v_mach_exit(unsigned long exit_core);
+extern void sun4v_mach_exit(unsigned long exit_code);
#endif
/* Domain services. */
diff --git a/include/asm-sparc64/ldc.h b/include/asm-sparc64/ldc.h
index a21996c6b155..8d17bd6bd5db 100644
--- a/include/asm-sparc64/ldc.h
+++ b/include/asm-sparc64/ldc.h
@@ -6,6 +6,8 @@
extern int ldom_domaining_enabled;
extern void ldom_set_var(const char *var, const char *value);
extern void ldom_reboot(const char *boot_command);
+extern void ldom_power_off(void);
+extern void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg);
/* The event handler will be evoked when link state changes
* or data becomes available on the receive side.
diff --git a/include/asm-sparc64/mdesc.h b/include/asm-sparc64/mdesc.h
index dc372df23fb3..e97c43133752 100644
--- a/include/asm-sparc64/mdesc.h
+++ b/include/asm-sparc64/mdesc.h
@@ -2,6 +2,7 @@
#define _SPARC64_MDESC_H
#include <linux/types.h>
+#include <linux/cpumask.h>
#include <asm/prom.h>
struct mdesc_handle;
@@ -60,6 +61,8 @@ extern u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc);
extern void mdesc_update(void);
+extern void mdesc_fill_in_cpu_data(cpumask_t mask);
+
extern void sun4v_mdesc_init(void);
#endif
diff --git a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h
index 4fb8c4bfb848..c42c5a035c73 100644
--- a/include/asm-sparc64/smp.h
+++ b/include/asm-sparc64/smp.h
@@ -29,9 +29,6 @@
#include <asm/bitops.h>
#include <asm/atomic.h>
-extern cpumask_t phys_cpu_present_map;
-#define cpu_possible_map phys_cpu_present_map
-
extern cpumask_t cpu_sibling_map[NR_CPUS];
extern cpumask_t cpu_core_map[NR_CPUS];
extern int sparc64_multi_core;
@@ -46,6 +43,11 @@ extern int hard_smp_processor_id(void);
extern void smp_fill_in_sib_core_maps(void);
extern unsigned char boot_cpu_id;
+#ifdef CONFIG_HOTPLUG_CPU
+extern int __cpu_disable(void);
+extern void __cpu_die(unsigned int cpu);
+#endif
+
#endif /* !(__ASSEMBLY__) */
#else