Commit c7754d46 authored by David S. Miller's avatar David S. Miller

[SPARC64]: Add hypervisor API negotiation and fix console bugs.

Hypervisor interfaces need to be negotiated in order to use
some API calls reliably.  So add a small set of interfaces
to request API versions and query current settings.

This allows us to fix some bugs in the hypervisor console:

1) If we can negotiate API group CORE of at least major 1
   minor 1 we can use con_read and con_write which can improve
   console performance quite a bit.

2) When we do a console write request, we should hold the
   spinlock around the whole request, not a byte at a time.
   What would happen is that it's easy for output from
   different cpus to get mixed with each other.

3) Use consistent udelay() based polling, udelay(1) each
   loop with a limit of 1000 polls to handle stuck hypervisor
   console.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7b104bcb
......@@ -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 \
......
......@@ -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
/* 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();
}
......@@ -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;
......@@ -300,6 +301,8 @@ void __init sun4v_patch(void)
p2++;
}
sun4v_hvapi_init();
}
#ifdef CONFIG_SMP
......
This diff is collapsed.
......@@ -940,6 +940,54 @@ struct hv_fault_status {
*/
#define HV_FAST_CONS_PUTCHAR 0x61
/* con_read()
* TRAP: HV_FAST_TRAP
* FUNCTION: HV_FAST_CONS_READ
* ARG0: buffer real address
* ARG1: buffer size in bytes
* RET0: status
* RET1: bytes read or BREAK or HUP
* ERRORS: EWOULDBLOCK No character available.
*
* Reads characters into a buffer from the console device. If no
* character is available then an EWOULDBLOCK error is returned.
* If a character is available, then the returned status is EOK
* and the number of bytes read into the given buffer is provided
* in RET1.
*
* A virtual BREAK is represented by the 64-bit RET1 value -1.
*
* A virtual HUP signal is represented by the 64-bit RET1 value -2.
*
* If BREAK or HUP are indicated, no bytes were read into buffer.
*/
#define HV_FAST_CONS_READ 0x62
/* con_write()
* TRAP: HV_FAST_TRAP
* FUNCTION: HV_FAST_CONS_WRITE
* ARG0: buffer real address
* ARG1: buffer size in bytes
* RET0: status
* RET1: bytes written
* ERRORS: EWOULDBLOCK Output buffer currently full, would block
*
* Send a characters in buffer to the console device. Breaks must be
* sent using con_putchar().
*/
#define HV_FAST_CONS_WRITE 0x63
#ifndef __ASSEMBLY__
extern long sun4v_con_getchar(long *status);
extern long sun4v_con_putchar(long c);
extern long sun4v_con_read(unsigned long buffer,
unsigned long size,
unsigned long *bytes_read);
extern unsigned long sun4v_con_write(unsigned long buffer,
unsigned long size,
unsigned long *bytes_written);
#endif
/* Trap trace services.
*
* The hypervisor provides a trap tracing capability for privileged
......@@ -2121,8 +2169,41 @@ struct hv_mmu_statistics {
#define HV_FAST_MMUSTAT_INFO 0x103
/* Function numbers for HV_CORE_TRAP. */
#define HV_CORE_VER 0x00
#define HV_CORE_SET_VER 0x00
#define HV_CORE_PUTCHAR 0x01
#define HV_CORE_EXIT 0x02
#define HV_CORE_GET_VER 0x03
/* Hypervisor API groups for use with HV_CORE_SET_VER and
* HV_CORE_GET_VER.
*/
#define HV_GRP_SUN4V 0x0000
#define HV_GRP_CORE 0x0001
#define HV_GRP_INTR 0x0002
#define HV_GRP_SOFT_STATE 0x0003
#define HV_GRP_PCI 0x0100
#define HV_GRP_LDOM 0x0101
#define HV_GRP_SVC_CHAN 0x0102
#define HV_GRP_NCS 0x0103
#define HV_GRP_NIAG_PERF 0x0200
#define HV_GRP_FIRE_PERF 0x0201
#define HV_GRP_DIAG 0x0300
#ifndef __ASSEMBLY__
extern unsigned long sun4v_get_version(unsigned long group,
unsigned long *major,
unsigned long *minor);
extern unsigned long sun4v_set_version(unsigned long group,
unsigned long major,
unsigned long minor,
unsigned long *actual_minor);
extern int sun4v_hvapi_register(unsigned long group, unsigned long major,
unsigned long *minor);
extern void sun4v_hvapi_unregister(unsigned long group);
extern int sun4v_hvapi_get(unsigned long group,
unsigned long *major,
unsigned long *minor);
#endif
#endif /* !(_SPARC64_HYPERVISOR_H) */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment