Skip to content

Commit

Permalink
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
Browse files Browse the repository at this point in the history
* master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6:
  [SPARC64]: Add hypervisor API negotiation and fix console bugs.
  • Loading branch information
Linus Torvalds committed May 17, 2007
2 parents 43c0f3d + c7754d4 commit e0f88db
Show file tree
Hide file tree
Showing 6 changed files with 574 additions and 73 deletions.
2 changes: 1 addition & 1 deletion arch/sparc64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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
visemul.o prom.o of_device.o hvapi.o

obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
Expand Down
94 changes: 94 additions & 0 deletions arch/sparc64/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -1843,3 +1843,97 @@ sun4v_cpu_state:
mov %o1, %o0
1: retl
nop

/* %o0: API group number
* %o1: pointer to unsigned long major number storage
* %o2: pointer to unsigned long minor number storage
*
* returns %o0: status
*/
.globl sun4v_get_version
sun4v_get_version:
mov HV_CORE_GET_VER, %o5
mov %o1, %o3
mov %o2, %o4
ta HV_CORE_TRAP
stx %o1, [%o3]
retl
stx %o2, [%o4]

/* %o0: API group number
* %o1: desired major number
* %o2: desired minor number
* %o3: pointer to unsigned long actual minor number storage
*
* returns %o0: status
*/
.globl sun4v_set_version
sun4v_set_version:
mov HV_CORE_SET_VER, %o5
mov %o3, %o4
ta HV_CORE_TRAP
retl
stx %o1, [%o4]

/* %o0: pointer to unsigned long status
*
* returns %o0: signed character
*/
.globl sun4v_con_getchar
sun4v_con_getchar:
mov %o0, %o4
mov HV_FAST_CONS_GETCHAR, %o5
clr %o0
clr %o1
ta HV_FAST_TRAP
stx %o0, [%o4]
retl
sra %o1, 0, %o0

/* %o0: signed long character
*
* returns %o0: status
*/
.globl sun4v_con_putchar
sun4v_con_putchar:
mov HV_FAST_CONS_PUTCHAR, %o5
ta HV_FAST_TRAP
retl
sra %o0, 0, %o0

/* %o0: buffer real address
* %o1: buffer size
* %o2: pointer to unsigned long bytes_read
*
* returns %o0: status
*/
.globl sun4v_con_read
sun4v_con_read:
mov %o2, %o4
mov HV_FAST_CONS_READ, %o5
ta HV_FAST_TRAP
brnz %o0, 1f
cmp %o1, -1 /* break */
be,a,pn %icc, 1f
mov %o1, %o0
cmp %o1, -2 /* hup */
be,a,pn %icc, 1f
mov %o1, %o0
stx %o1, [%o4]
1: retl
nop

/* %o0: buffer real address
* %o1: buffer size
* %o2: pointer to unsigned long bytes_written
*
* returns %o0: status
*/
.globl sun4v_con_write
sun4v_con_write:
mov %o2, %o4
mov HV_FAST_CONS_WRITE, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
189 changes: 189 additions & 0 deletions arch/sparc64/kernel/hvapi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/* hvapi.c: Hypervisor API management.
*
* Copyright (C) 2007 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>

#include <asm/hypervisor.h>
#include <asm/oplib.h>

/* If the hypervisor indicates that the API setting
* calls are unsupported, by returning HV_EBADTRAP or
* HV_ENOTSUPPORTED, we assume that API groups with the
* PRE_API flag set are major 1 minor 0.
*/
struct api_info {
unsigned long group;
unsigned long major;
unsigned long minor;
unsigned int refcnt;
unsigned int flags;
#define FLAG_PRE_API 0x00000001
};

static struct api_info api_table[] = {
{ .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API },
{ .group = HV_GRP_CORE, .flags = FLAG_PRE_API },
{ .group = HV_GRP_INTR, },
{ .group = HV_GRP_SOFT_STATE, },
{ .group = HV_GRP_PCI, .flags = FLAG_PRE_API },
{ .group = HV_GRP_LDOM, },
{ .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API },
{ .group = HV_GRP_NCS, .flags = FLAG_PRE_API },
{ .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API },
{ .group = HV_GRP_FIRE_PERF, },
{ .group = HV_GRP_DIAG, .flags = FLAG_PRE_API },
};

static DEFINE_SPINLOCK(hvapi_lock);

static struct api_info *__get_info(unsigned long group)
{
int i;

for (i = 0; i < ARRAY_SIZE(api_table); i++) {
if (api_table[i].group == group)
return &api_table[i];
}
return NULL;
}

static void __get_ref(struct api_info *p)
{
p->refcnt++;
}

static void __put_ref(struct api_info *p)
{
if (--p->refcnt == 0) {
unsigned long ignore;

sun4v_set_version(p->group, 0, 0, &ignore);
p->major = p->minor = 0;
}
}

/* Register a hypervisor API specification. It indicates the
* API group and desired major+minor.
*
* If an existing API registration exists '0' (success) will
* be returned if it is compatible with the one being registered.
* Otherwise a negative error code will be returned.
*
* Otherwise an attempt will be made to negotiate the requested
* API group/major/minor with the hypervisor, and errors returned
* if that does not succeed.
*/
int sun4v_hvapi_register(unsigned long group, unsigned long major,
unsigned long *minor)
{
struct api_info *p;
unsigned long flags;
int ret;

spin_lock_irqsave(&hvapi_lock, flags);
p = __get_info(group);
ret = -EINVAL;
if (p) {
if (p->refcnt) {
ret = -EINVAL;
if (p->major == major) {
*minor = p->minor;
ret = 0;
}
} else {
unsigned long actual_minor;
unsigned long hv_ret;

hv_ret = sun4v_set_version(group, major, *minor,
&actual_minor);
ret = -EINVAL;
if (hv_ret == HV_EOK) {
*minor = actual_minor;
p->major = major;
p->minor = actual_minor;
ret = 0;
} else if (hv_ret == HV_EBADTRAP ||
HV_ENOTSUPPORTED) {
if (p->flags & FLAG_PRE_API) {
if (major == 1) {
p->major = 1;
p->minor = 0;
*minor = 0;
ret = 0;
}
}
}
}

if (ret == 0)
__get_ref(p);
}
spin_unlock_irqrestore(&hvapi_lock, flags);

return ret;
}
EXPORT_SYMBOL(sun4v_hvapi_register);

void sun4v_hvapi_unregister(unsigned long group)
{
struct api_info *p;
unsigned long flags;

spin_lock_irqsave(&hvapi_lock, flags);
p = __get_info(group);
if (p)
__put_ref(p);
spin_unlock_irqrestore(&hvapi_lock, flags);
}
EXPORT_SYMBOL(sun4v_hvapi_unregister);

int sun4v_hvapi_get(unsigned long group,
unsigned long *major,
unsigned long *minor)
{
struct api_info *p;
unsigned long flags;
int ret;

spin_lock_irqsave(&hvapi_lock, flags);
ret = -EINVAL;
p = __get_info(group);
if (p && p->refcnt) {
*major = p->major;
*minor = p->minor;
ret = 0;
}
spin_unlock_irqrestore(&hvapi_lock, flags);

return ret;
}
EXPORT_SYMBOL(sun4v_hvapi_get);

void __init sun4v_hvapi_init(void)
{
unsigned long group, major, minor;

group = HV_GRP_SUN4V;
major = 1;
minor = 0;
if (sun4v_hvapi_register(group, major, &minor))
goto bad;

group = HV_GRP_CORE;
major = 1;
minor = 1;
if (sun4v_hvapi_register(group, major, &minor))
goto bad;

return;

bad:
prom_printf("HVAPI: Cannot register API group "
"%lx with major(%u) minor(%u)\n",
group, major, minor);
prom_halt();
}
3 changes: 3 additions & 0 deletions arch/sparc64/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ void __init per_cpu_patch(void)

void __init sun4v_patch(void)
{
extern void sun4v_hvapi_init(void);
struct sun4v_1insn_patch_entry *p1;
struct sun4v_2insn_patch_entry *p2;

Expand Down Expand Up @@ -300,6 +301,8 @@ void __init sun4v_patch(void)

p2++;
}

sun4v_hvapi_init();
}

#ifdef CONFIG_SMP
Expand Down
Loading

0 comments on commit e0f88db

Please sign in to comment.