// SPDX-License-Identifier: GPL-2.0-or-later
/*
* hvcserver . c
* Copyright ( C ) 2004 Ryan S Arnold , IBM Corporation
*
* PPC64 virtual I / O console server support .
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm /hvcall.h>
#include <asm /hvcserver.h>
#include <asm /io.h>
#define HVCS_ARCH_VERSION "1.0.0"
MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>" );
MODULE_DESCRIPTION("IBM hvcs ppc64 API" );
MODULE_LICENSE("GPL" );
MODULE_VERSION(HVCS_ARCH_VERSION);
/*
* Convert arch specific return codes into relevant errnos . The hvcs
* functions aren ' t performance sensitive , so this conversion isn ' t an
* issue .
*/
static int hvcs_convert(long to_convert)
{
switch (to_convert) {
case H_SUCCESS:
return 0 ;
case H_PARAMETER:
return -EINVAL;
case H_HARDWARE:
return -EIO;
case H_BUSY:
case H_LONG_BUSY_ORDER_1_MSEC:
case H_LONG_BUSY_ORDER_10_MSEC:
case H_LONG_BUSY_ORDER_100_MSEC:
case H_LONG_BUSY_ORDER_1_SEC:
case H_LONG_BUSY_ORDER_10_SEC:
case H_LONG_BUSY_ORDER_100_SEC:
return -EBUSY;
case H_FUNCTION:
default :
return -EPERM;
}
}
/**
* hvcs_free_partner_info - free pi allocated by hvcs_get_partner_info
* @ head : list_head pointer for an allocated list of partner info structs to
* free .
*
* This function is used to free the partner info list that was returned by
* calling hvcs_get_partner_info ( ) .
*/
int hvcs_free_partner_info(struct list_head *head)
{
struct hvcs_partner_info *pi;
struct list_head *element;
if (!head)
return -EINVAL;
while (!list_empty(head)) {
element = head->next;
pi = list_entry(element, struct hvcs_partner_info, node);
list_del(element);
kfree(pi);
}
return 0 ;
}
EXPORT_SYMBOL(hvcs_free_partner_info);
/* Helper function for hvcs_get_partner_info */
static int hvcs_next_partner(uint32_t unit_address,
unsigned long last_p_partition_ID,
unsigned long last_p_unit_address, unsigned long *pi_buff)
{
long retval;
retval = plpar_hcall_norets(H_VTERM_PARTNER_INFO, unit_address,
last_p_partition_ID,
last_p_unit_address, virt_to_phys(pi_buff));
return hvcs_convert(retval);
}
/**
* hvcs_get_partner_info - Get all of the partner info for a vty - server adapter
* @ unit_address : The unit_address of the vty - server adapter for which this
* function is fetching partner info .
* @ head : An initialized list_head pointer to an empty list to use to return the
* list of partner info fetched from the hypervisor to the caller .
* @ pi_buff : A page sized buffer pre - allocated prior to calling this function
* that is to be used to be used by firmware as an iterator to keep track
* of the partner info retrieval .
*
* This function returns non - zero on success , or if there is no partner info .
*
* The pi_buff is pre - allocated prior to calling this function because this
* function may be called with a spin_lock held and kmalloc of a page is not
* recommended as GFP_ATOMIC .
*
* The first long of this buffer is used to store a partner unit address . The
* second long is used to store a partner partition ID and starting at
* pi_buff [ 2 ] is the 79 character Converged Location Code ( diff size than the
* unsigned longs , hence the casting mumbo jumbo you see later ) .
*
* Invocation of this function should always be followed by an invocation of
* hvcs_free_partner_info ( ) using a pointer to the SAME list head instance
* that was passed as a parameter to this function .
*/
int hvcs_get_partner_info(uint32_t unit_address, struct list_head *head,
unsigned long *pi_buff)
{
/*
* Dealt with as longs because of the hcall interface even though the
* values are uint32_t .
*/
unsigned long last_p_partition_ID;
unsigned long last_p_unit_address;
struct hvcs_partner_info *next_partner_info = NULL;
int more = 1 ;
int retval;
/* invalid parameters */
if (!head || !pi_buff)
return -EINVAL;
memset(pi_buff, 0 x00, PAGE_SIZE);
last_p_partition_ID = last_p_unit_address = ~0 UL;
INIT_LIST_HEAD(head);
do {
retval = hvcs_next_partner(unit_address, last_p_partition_ID,
last_p_unit_address, pi_buff);
if (retval) {
/*
* Don ' t indicate that we ' ve failed if we have
* any list elements .
*/
if (!list_empty(head))
return 0 ;
return retval;
}
last_p_partition_ID = be64_to_cpu(pi_buff[0 ]);
last_p_unit_address = be64_to_cpu(pi_buff[1 ]);
/* This indicates that there are no further partners */
if (last_p_partition_ID == ~0 UL
&& last_p_unit_address == ~0 UL)
break ;
/* This is a very small struct and will be freed soon in
* hvcs_free_partner_info(). */
next_partner_info = kmalloc(sizeof (struct hvcs_partner_info),
GFP_ATOMIC);
if (!next_partner_info) {
printk(KERN_WARNING "HVCONSOLE: kmalloc() failed to"
" allocate partner info struct.\n" );
hvcs_free_partner_info(head);
return -ENOMEM;
}
next_partner_info->unit_address
= (unsigned int )last_p_unit_address;
next_partner_info->partition_ID
= (unsigned int )last_p_partition_ID;
/* copy the Null-term char too */
strscpy(&next_partner_info->location_code[0 ],
(char *)&pi_buff[2 ],
sizeof (next_partner_info->location_code));
list_add_tail(&(next_partner_info->node), head);
next_partner_info = NULL;
} while (more);
return 0 ;
}
EXPORT_SYMBOL(hvcs_get_partner_info);
/**
* hvcs_register_connection - establish a connection between this vty - server and
* a vty .
* @ unit_address : The unit address of the vty - server adapter that is to be
* establish a connection .
* @ p_partition_ID : The partition ID of the vty adapter that is to be connected .
* @ p_unit_address : The unit address of the vty adapter to which the vty - server
* is to be connected .
*
* If this function is called once and - EINVAL is returned it may
* indicate that the partner info needs to be refreshed for the
* target unit address at which point the caller must invoke
* hvcs_get_partner_info ( ) and then call this function again . If ,
* for a second time , - EINVAL is returned then it indicates that
* there is probably already a partner connection registered to a
* different vty - server adapter . It is also possible that a second
* - EINVAL may indicate that one of the parms is not valid , for
* instance if the link was removed between the vty - server adapter
* and the vty adapter that you are trying to open . Don ' t shoot the
* messenger . Firmware implemented it this way .
*/
int hvcs_register_connection( uint32_t unit_address,
uint32_t p_partition_ID, uint32_t p_unit_address)
{
long retval;
retval = plpar_hcall_norets(H_REGISTER_VTERM, unit_address,
p_partition_ID, p_unit_address);
return hvcs_convert(retval);
}
EXPORT_SYMBOL(hvcs_register_connection);
/**
* hvcs_free_connection - free the connection between a vty - server and vty
* @ unit_address : The unit address of the vty - server that is to have its
* connection severed .
*
* This function is used to free the partner connection between a vty - server
* adapter and a vty adapter .
*
* If - EBUSY is returned continue to call this function until 0 is returned .
*/
int hvcs_free_connection(uint32_t unit_address)
{
long retval;
retval = plpar_hcall_norets(H_FREE_VTERM, unit_address);
return hvcs_convert(retval);
}
EXPORT_SYMBOL(hvcs_free_connection);
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland