summaryrefslogtreecommitdiffstats
path: root/sys/i386
diff options
context:
space:
mode:
authorluoqi <luoqi@FreeBSD.org>1999-12-06 04:53:08 +0000
committerluoqi <luoqi@FreeBSD.org>1999-12-06 04:53:08 +0000
commit5c9244cd12f812687a0e1da3fa8fd69c320bdb42 (patch)
tree3d2bdc635a51af92e9000f2fe294d685906d5387 /sys/i386
parent5851b69ff706eaa8e3a487bdfbbf3fe0177ed46a (diff)
downloadFreeBSD-src-5c9244cd12f812687a0e1da3fa8fd69c320bdb42.zip
FreeBSD-src-5c9244cd12f812687a0e1da3fa8fd69c320bdb42.tar.gz
User ldt sharing.
Diffstat (limited to 'sys/i386')
-rw-r--r--sys/i386/i386/genassym.c2
-rw-r--r--sys/i386/i386/machdep.c12
-rw-r--r--sys/i386/i386/sys_machdep.c255
-rw-r--r--sys/i386/i386/vm_machdep.c52
-rw-r--r--sys/i386/include/pcb.h9
-rw-r--r--sys/i386/include/pcb_ext.h18
6 files changed, 220 insertions, 128 deletions
diff --git a/sys/i386/i386/genassym.c b/sys/i386/i386/genassym.c
index d137b12..75f278d 100644
--- a/sys/i386/i386/genassym.c
+++ b/sys/i386/i386/genassym.c
@@ -120,7 +120,9 @@ main()
printf("#define\tPCB_EBX %#x\n", OS(pcb, pcb_ebx));
printf("#define\tPCB_EIP %#x\n", OS(pcb, pcb_eip));
printf("#define\tTSS_ESP0 %#x\n", OS(i386tss, tss_esp0));
+#ifdef USER_LDT
printf("#define\tPCB_USERLDT %#x\n", OS(pcb, pcb_ldt));
+#endif
printf("#define\tPCB_GS %#x\n", OS(pcb, pcb_gs));
printf("#define\tPCB_DR0 %#x\n", OS(pcb, pcb_dr0));
printf("#define\tPCB_DR1 %#x\n", OS(pcb, pcb_dr1));
diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c
index 645b702..dfe6a87 100644
--- a/sys/i386/i386/machdep.c
+++ b/sys/i386/i386/machdep.c
@@ -61,11 +61,11 @@
#include <sys/signalvar.h>
#include <sys/kernel.h>
#include <sys/linker.h>
+#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/callout.h>
-#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/msgbuf.h>
#include <sys/sysent.h>
@@ -1006,15 +1006,7 @@ setregs(p, entry, stack, ps_strings)
#ifdef USER_LDT
/* was i386_user_cleanup() in NetBSD */
- if (pcb->pcb_ldt) {
- if (pcb == curpcb) {
- lldt(_default_ldt);
- currentldt = _default_ldt;
- }
- kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
- pcb->pcb_ldt_len * sizeof(union descriptor));
- pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0;
- }
+ user_ldt_free(pcb);
#endif
bzero((char *)regs, sizeof(struct trapframe));
diff --git a/sys/i386/i386/sys_machdep.c b/sys/i386/i386/sys_machdep.c
index f9227dc..e9c7ddf 100644
--- a/sys/i386/i386/sys_machdep.c
+++ b/sys/i386/i386/sys_machdep.c
@@ -41,6 +41,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
+#include <sys/malloc.h>
#include <sys/proc.h>
#include <vm/vm.h>
@@ -65,7 +66,6 @@
#ifdef USER_LDT
-void set_user_ldt __P((struct pcb *pcb));
static int i386_get_ldt __P((struct proc *, char *));
static int i386_set_ldt __P((struct proc *, char *));
#endif
@@ -136,7 +136,7 @@ i386_extend_pcb(struct proc *p)
p->p_addr->u_pcb.pcb_ext = ext;
bzero(ext, sizeof(struct pcb_ext));
ext->ext_tss.tss_esp0 = (unsigned)p->p_addr + ctob(UPAGES) - 16;
- ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
+ ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL);
/*
* The last byte of the i/o map must be followed by an 0xff byte.
* We arbitrarily allocate 16 bytes here, to keep the starting
@@ -174,8 +174,8 @@ i386_set_ioperm(p, args)
if ((error = copyin(args, &ua, sizeof(struct i386_ioperm_args))) != 0)
return (error);
- if ((error = suser(p)) != 0)
- return (error);
+ if ((error = suser(p)) != 0)
+ return (error);
if (securelevel > 0)
return (EPERM);
/*
@@ -247,17 +247,77 @@ done:
void
set_user_ldt(struct pcb *pcb)
{
- gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)pcb->pcb_ldt;
- gdt_segs[GUSERLDT_SEL].ssd_limit = (pcb->pcb_ldt_len * sizeof(union descriptor)) - 1;
+ struct pcb_ldt *pcb_ldt;
+
+ if (pcb != curpcb)
+ return;
+
+ pcb_ldt = pcb->pcb_ldt;
#ifdef SMP
- ssdtosd(&gdt_segs[GUSERLDT_SEL], &gdt[cpuid * NGDT + GUSERLDT_SEL].sd);
+ gdt[cpuid * NGDT + GUSERLDT_SEL].sd = pcb_ldt->ldt_sd;
#else
- ssdtosd(&gdt_segs[GUSERLDT_SEL], &gdt[GUSERLDT_SEL].sd);
+ gdt[GUSERLDT_SEL].sd = pcb_ldt->ldt_sd;
#endif
lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
currentldt = GSEL(GUSERLDT_SEL, SEL_KPL);
}
+struct pcb_ldt *
+user_ldt_alloc(struct pcb *pcb, int len)
+{
+ struct pcb_ldt *pcb_ldt, *new_ldt;
+
+ MALLOC(new_ldt, struct pcb_ldt *, sizeof(struct pcb_ldt),
+ M_SUBPROC, M_WAITOK);
+ if (new_ldt == NULL)
+ return NULL;
+
+ new_ldt->ldt_len = len = NEW_MAX_LD(len);
+ new_ldt->ldt_base = (caddr_t)kmem_alloc(kernel_map,
+ len * sizeof(union descriptor));
+ if (new_ldt->ldt_base == NULL) {
+ FREE(new_ldt, M_SUBPROC);
+ return NULL;
+ }
+ new_ldt->ldt_refcnt = 1;
+ new_ldt->ldt_active = 0;
+
+ gdt_segs[GUSERLDT_SEL].ssd_base = (unsigned)new_ldt->ldt_base;
+ gdt_segs[GUSERLDT_SEL].ssd_limit = len * sizeof(union descriptor) - 1;
+ ssdtosd(&gdt_segs[GUSERLDT_SEL], &new_ldt->ldt_sd);
+
+ if ((pcb_ldt = pcb->pcb_ldt)) {
+ if (len > pcb_ldt->ldt_len)
+ len = pcb_ldt->ldt_len;
+ bcopy(pcb_ldt->ldt_base, new_ldt->ldt_base,
+ len * sizeof(union descriptor));
+ } else {
+ bcopy(ldt, new_ldt->ldt_base, sizeof(ldt));
+ }
+ return new_ldt;
+}
+
+void
+user_ldt_free(struct pcb *pcb)
+{
+ struct pcb_ldt *pcb_ldt = pcb->pcb_ldt;
+
+ if (pcb_ldt == NULL)
+ return;
+
+ if (pcb == curpcb) {
+ lldt(_default_ldt);
+ currentldt = _default_ldt;
+ }
+
+ if (--pcb_ldt->ldt_refcnt == 0) {
+ kmem_free(kernel_map, (vm_offset_t)pcb_ldt->ldt_base,
+ pcb_ldt->ldt_len * sizeof(union descriptor));
+ FREE(pcb_ldt, M_SUBPROC);
+ }
+ pcb->pcb_ldt = NULL;
+}
+
static int
i386_get_ldt(p, args)
struct proc *p;
@@ -265,6 +325,7 @@ i386_get_ldt(p, args)
{
int error = 0;
struct pcb *pcb = &p->p_addr->u_pcb;
+ struct pcb_ldt *pcb_ldt = pcb->pcb_ldt;
int nldt, num;
union descriptor *lp;
int s;
@@ -284,10 +345,10 @@ i386_get_ldt(p, args)
s = splhigh();
- if (pcb->pcb_ldt) {
- nldt = pcb->pcb_ldt_len;
+ if (pcb_ldt) {
+ nldt = pcb_ldt->ldt_len;
num = min(uap->num, nldt);
- lp = &((union descriptor *)(pcb->pcb_ldt))[uap->start];
+ lp = &((union descriptor *)(pcb_ldt->ldt_base))[uap->start];
} else {
nldt = sizeof(ldt)/sizeof(ldt[0]);
num = min(uap->num, nldt);
@@ -312,8 +373,9 @@ i386_set_ldt(p, args)
char *args;
{
int error = 0, i, n;
- int largest_ld;
+ int largest_ld;
struct pcb *pcb = &p->p_addr->u_pcb;
+ struct pcb_ldt *pcb_ldt = pcb->pcb_ldt;
int s;
struct i386_ldt_args ua, *uap = &ua;
@@ -325,36 +387,37 @@ i386_set_ldt(p, args)
uap->start, uap->num, (void *)uap->descs);
#endif
- /* verify range of descriptors to modify */
- if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
- (uap->num > MAX_LD))
- {
- return(EINVAL);
- }
- largest_ld = uap->start + uap->num - 1;
- if (largest_ld >= MAX_LD)
- return(EINVAL);
-
- /* allocate user ldt */
- if (!pcb->pcb_ldt || (largest_ld >= pcb->pcb_ldt_len)) {
- union descriptor *new_ldt = (union descriptor *)kmem_alloc(
- kernel_map, SIZE_FROM_LARGEST_LD(largest_ld));
- if (new_ldt == NULL) {
- return ENOMEM;
- }
- if (pcb->pcb_ldt) {
- bcopy(pcb->pcb_ldt, new_ldt, pcb->pcb_ldt_len
- * sizeof(union descriptor));
- kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
- pcb->pcb_ldt_len * sizeof(union descriptor));
- } else {
- bcopy(ldt, new_ldt, sizeof(ldt));
- }
- pcb->pcb_ldt = (caddr_t)new_ldt;
- pcb->pcb_ldt_len = NEW_MAX_LD(largest_ld);
- if (pcb == curpcb)
- set_user_ldt(pcb);
- }
+ /* verify range of descriptors to modify */
+ if ((uap->start < 0) || (uap->start >= MAX_LD) || (uap->num < 0) ||
+ (uap->num > MAX_LD))
+ {
+ return(EINVAL);
+ }
+ largest_ld = uap->start + uap->num - 1;
+ if (largest_ld >= MAX_LD)
+ return(EINVAL);
+
+ /* allocate user ldt */
+ if (!pcb_ldt || largest_ld >= pcb_ldt->ldt_len) {
+ struct pcb_ldt *new_ldt = user_ldt_alloc(pcb, largest_ld);
+ if (new_ldt == NULL)
+ return ENOMEM;
+ if (pcb_ldt) {
+ pcb_ldt->ldt_sd = new_ldt->ldt_sd;
+ kmem_free(kernel_map, (vm_offset_t)pcb_ldt->ldt_base,
+ pcb_ldt->ldt_len * sizeof(union descriptor));
+ pcb_ldt->ldt_base = new_ldt->ldt_base;
+ pcb_ldt->ldt_len = new_ldt->ldt_len;
+ FREE(new_ldt, M_SUBPROC);
+ } else
+ pcb->pcb_ldt = pcb_ldt = new_ldt;
+#ifdef SMP
+ /* signal other cpus to reload ldt */
+ smp_rendezvous(NULL, (void (*)(void *))set_user_ldt, NULL, pcb);
+#else
+ set_user_ldt(pcb);
+#endif
+ }
/* Check descriptors for access violations */
for (i = 0, n = uap->start; i < uap->num; i++, n++) {
@@ -365,70 +428,70 @@ i386_set_ldt(p, args)
return(error);
switch (desc.sd.sd_type) {
- case SDT_SYSNULL: /* system null */
- desc.sd.sd_p = 0;
- break;
- case SDT_SYS286TSS: /* system 286 TSS available */
- case SDT_SYSLDT: /* system local descriptor table */
- case SDT_SYS286BSY: /* system 286 TSS busy */
- case SDT_SYSTASKGT: /* system task gate */
- case SDT_SYS286IGT: /* system 286 interrupt gate */
- case SDT_SYS286TGT: /* system 286 trap gate */
- case SDT_SYSNULL2: /* undefined by Intel */
- case SDT_SYS386TSS: /* system 386 TSS available */
- case SDT_SYSNULL3: /* undefined by Intel */
- case SDT_SYS386BSY: /* system 386 TSS busy */
- case SDT_SYSNULL4: /* undefined by Intel */
- case SDT_SYS386IGT: /* system 386 interrupt gate */
- case SDT_SYS386TGT: /* system 386 trap gate */
- case SDT_SYS286CGT: /* system 286 call gate */
- case SDT_SYS386CGT: /* system 386 call gate */
- /* I can't think of any reason to allow a user proc
- * to create a segment of these types. They are
- * for OS use only.
- */
- return EACCES;
-
- /* memory segment types */
- case SDT_MEMEC: /* memory execute only conforming */
- case SDT_MEMEAC: /* memory execute only accessed conforming */
- case SDT_MEMERC: /* memory execute read conforming */
- case SDT_MEMERAC: /* memory execute read accessed conforming */
- /* Must be "present" if executable and conforming. */
- if (desc.sd.sd_p == 0)
- return (EACCES);
- break;
- case SDT_MEMRO: /* memory read only */
- case SDT_MEMROA: /* memory read only accessed */
- case SDT_MEMRW: /* memory read write */
- case SDT_MEMRWA: /* memory read write accessed */
- case SDT_MEMROD: /* memory read only expand dwn limit */
- case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
- case SDT_MEMRWD: /* memory read write expand dwn limit */
- case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
- case SDT_MEME: /* memory execute only */
- case SDT_MEMEA: /* memory execute only accessed */
- case SDT_MEMER: /* memory execute read */
- case SDT_MEMERA: /* memory execute read accessed */
+ case SDT_SYSNULL: /* system null */
+ desc.sd.sd_p = 0;
+ break;
+ case SDT_SYS286TSS: /* system 286 TSS available */
+ case SDT_SYSLDT: /* system local descriptor table */
+ case SDT_SYS286BSY: /* system 286 TSS busy */
+ case SDT_SYSTASKGT: /* system task gate */
+ case SDT_SYS286IGT: /* system 286 interrupt gate */
+ case SDT_SYS286TGT: /* system 286 trap gate */
+ case SDT_SYSNULL2: /* undefined by Intel */
+ case SDT_SYS386TSS: /* system 386 TSS available */
+ case SDT_SYSNULL3: /* undefined by Intel */
+ case SDT_SYS386BSY: /* system 386 TSS busy */
+ case SDT_SYSNULL4: /* undefined by Intel */
+ case SDT_SYS386IGT: /* system 386 interrupt gate */
+ case SDT_SYS386TGT: /* system 386 trap gate */
+ case SDT_SYS286CGT: /* system 286 call gate */
+ case SDT_SYS386CGT: /* system 386 call gate */
+ /* I can't think of any reason to allow a user proc
+ * to create a segment of these types. They are
+ * for OS use only.
+ */
+ return EACCES;
+
+ /* memory segment types */
+ case SDT_MEMEC: /* memory execute only conforming */
+ case SDT_MEMEAC: /* memory execute only accessed conforming */
+ case SDT_MEMERC: /* memory execute read conforming */
+ case SDT_MEMERAC: /* memory execute read accessed conforming */
+ /* Must be "present" if executable and conforming. */
+ if (desc.sd.sd_p == 0)
+ return (EACCES);
+ break;
+ case SDT_MEMRO: /* memory read only */
+ case SDT_MEMROA: /* memory read only accessed */
+ case SDT_MEMRW: /* memory read write */
+ case SDT_MEMRWA: /* memory read write accessed */
+ case SDT_MEMROD: /* memory read only expand dwn limit */
+ case SDT_MEMRODA: /* memory read only expand dwn lim accessed */
+ case SDT_MEMRWD: /* memory read write expand dwn limit */
+ case SDT_MEMRWDA: /* memory read write expand dwn lim acessed */
+ case SDT_MEME: /* memory execute only */
+ case SDT_MEMEA: /* memory execute only accessed */
+ case SDT_MEMER: /* memory execute read */
+ case SDT_MEMERA: /* memory execute read accessed */
break;
default:
return(EINVAL);
/*NOTREACHED*/
}
-
- /* Only user (ring-3) descriptors may be present. */
- if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL))
- return (EACCES);
+
+ /* Only user (ring-3) descriptors may be present. */
+ if ((desc.sd.sd_p != 0) && (desc.sd.sd_dpl != SEL_UPL))
+ return (EACCES);
}
s = splhigh();
/* Fill in range */
- error = copyin(uap->descs,
- &((union descriptor *)(pcb->pcb_ldt))[uap->start],
- uap->num * sizeof(union descriptor));
- if (!error)
- p->p_retval[0] = uap->start;
+ error = copyin(uap->descs,
+ &((union descriptor *)(pcb_ldt->ldt_base))[uap->start],
+ uap->num * sizeof(union descriptor));
+ if (!error)
+ p->p_retval[0] = uap->start;
splx(s);
return(error);
diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c
index 9ca7d3a..d7fa73b1 100644
--- a/sys/i386/i386/vm_machdep.c
+++ b/sys/i386/i386/vm_machdep.c
@@ -49,13 +49,14 @@
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/proc.h>
#include <sys/malloc.h>
+#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/vmmeter.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
+#include <sys/unistd.h>
#include <machine/clock.h>
#include <machine/cpu.h>
@@ -63,6 +64,9 @@
#ifdef SMP
#include <machine/smp.h>
#endif
+#include <machine/pcb.h>
+#include <machine/pcb_ext.h>
+#include <machine/vm86.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
@@ -110,10 +114,28 @@ vm_fault_quick(v, prot)
* ready to run and return to user mode.
*/
void
-cpu_fork(p1, p2)
+cpu_fork(p1, p2, flags)
register struct proc *p1, *p2;
+ int flags;
{
- struct pcb *pcb2 = &p2->p_addr->u_pcb;
+ struct pcb *pcb2;
+
+ if ((flags & RFPROC) == 0) {
+#ifdef USER_LDT
+ if ((flags & RFMEM) == 0) {
+ /* unshare user LDT */
+ struct pcb *pcb1 = &p1->p_addr->u_pcb;
+ struct pcb_ldt *pcb_ldt = pcb1->pcb_ldt;
+ if (pcb_ldt && pcb_ldt->ldt_refcnt > 1) {
+ pcb_ldt = user_ldt_alloc(pcb1,pcb_ldt->ldt_len);
+ user_ldt_free(pcb1);
+ pcb1->pcb_ldt = pcb_ldt;
+ set_user_ldt(pcb1);
+ }
+ }
+#endif
+ return;
+ }
#if NNPX > 0
/* Ensure that p1's pcb is up to date. */
@@ -123,6 +145,7 @@ cpu_fork(p1, p2)
/* Copy p1's pcb. */
p2->p_addr->u_pcb = p1->p_addr->u_pcb;
+ pcb2 = &p2->p_addr->u_pcb;
/*
* Create a new fresh stack for the new process.
@@ -146,7 +169,6 @@ cpu_fork(p1, p2)
pcb2->pcb_eip = (int)fork_trampoline;
/*
* pcb2->pcb_ldt: duplicated below, if necessary.
- * pcb2->pcb_ldt_len: cloned above.
* pcb2->pcb_savefpu: cloned above.
* pcb2->pcb_flags: cloned above (always 0 here?).
* pcb2->pcb_onfault: cloned above (always NULL here?).
@@ -163,12 +185,12 @@ cpu_fork(p1, p2)
#ifdef USER_LDT
/* Copy the LDT, if necessary. */
if (pcb2->pcb_ldt != 0) {
- union descriptor *new_ldt;
- size_t len = pcb2->pcb_ldt_len * sizeof(union descriptor);
-
- new_ldt = (union descriptor *)kmem_alloc(kernel_map, len);
- bcopy(pcb2->pcb_ldt, new_ldt, len);
- pcb2->pcb_ldt = (caddr_t)new_ldt;
+ if (flags & RFMEM) {
+ pcb2->pcb_ldt->ldt_refcnt++;
+ } else {
+ pcb2->pcb_ldt = user_ldt_alloc(pcb2,
+ pcb2->pcb_ldt->ldt_len);
+ }
}
#endif
@@ -222,15 +244,7 @@ cpu_exit(p)
pcb->pcb_ext = 0;
}
#ifdef USER_LDT
- if (pcb->pcb_ldt != 0) {
- if (pcb == curpcb) {
- lldt(_default_ldt);
- currentldt = _default_ldt;
- }
- kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
- pcb->pcb_ldt_len * sizeof(union descriptor));
- pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0;
- }
+ user_ldt_free(pcb);
#endif
cnt.v_swtch++;
cpu_switch(p);
diff --git a/sys/i386/include/pcb.h b/sys/i386/include/pcb.h
index ed3044c..dde7281 100644
--- a/sys/i386/include/pcb.h
+++ b/sys/i386/include/pcb.h
@@ -62,8 +62,11 @@ struct pcb {
int pcb_dr6;
int pcb_dr7;
- caddr_t pcb_ldt; /* per process (user) LDT */
- int pcb_ldt_len; /* number of LDT entries */
+#ifdef USER_LDT
+ struct pcb_ldt *pcb_ldt; /* per process (user) LDT */
+#else
+ struct pcb_ldt *pcb_ldt_dontuse;
+#endif
struct save87 pcb_savefpu; /* floating point state for 287/387 */
u_char pcb_flags;
#define FP_SOFTFP 0x01 /* process using software fltng pnt emulator */
@@ -76,7 +79,7 @@ struct pcb {
#endif
int pcb_gs;
struct pcb_ext *pcb_ext; /* optional pcb extension */
- u_long __pcb_spare[2]; /* adjust to avoid core dump size changes */
+ u_long __pcb_spare[3]; /* adjust to avoid core dump size changes */
};
/*
diff --git a/sys/i386/include/pcb_ext.h b/sys/i386/include/pcb_ext.h
index 154cf94..42e8abf 100644
--- a/sys/i386/include/pcb_ext.h
+++ b/sys/i386/include/pcb_ext.h
@@ -43,4 +43,22 @@ struct pcb_ext {
struct vm86_kernel ext_vm86; /* vm86 area */
};
+struct pcb_ldt {
+ caddr_t ldt_base;
+ int ldt_len;
+ int ldt_refcnt;
+ u_long ldt_active;
+ struct segment_descriptor ldt_sd;
+};
+
+#ifdef KERNEL
+
+#ifdef USER_LDT
+void set_user_ldt __P((struct pcb *));
+struct pcb_ldt *user_ldt_alloc __P((struct pcb *, int));
+void user_ldt_free __P((struct pcb *));
+#endif
+
+#endif
+
#endif /* _I386_PCB_EXT_H_ */
OpenPOWER on IntegriCloud