Path: blob/master/arch/powerpc/platforms/pseries/hvcserver.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* hvcserver.c3* Copyright (C) 2004 Ryan S Arnold, IBM Corporation4*5* PPC64 virtual I/O console server support.6*/78#include <linux/kernel.h>9#include <linux/list.h>10#include <linux/module.h>11#include <linux/slab.h>12#include <linux/string.h>1314#include <asm/hvcall.h>15#include <asm/hvcserver.h>16#include <asm/io.h>1718#define HVCS_ARCH_VERSION "1.0.0"1920MODULE_AUTHOR("Ryan S. Arnold <[email protected]>");21MODULE_DESCRIPTION("IBM hvcs ppc64 API");22MODULE_LICENSE("GPL");23MODULE_VERSION(HVCS_ARCH_VERSION);2425/*26* Convert arch specific return codes into relevant errnos. The hvcs27* functions aren't performance sensitive, so this conversion isn't an28* issue.29*/30static int hvcs_convert(long to_convert)31{32switch (to_convert) {33case H_SUCCESS:34return 0;35case H_PARAMETER:36return -EINVAL;37case H_HARDWARE:38return -EIO;39case H_BUSY:40case H_LONG_BUSY_ORDER_1_MSEC:41case H_LONG_BUSY_ORDER_10_MSEC:42case H_LONG_BUSY_ORDER_100_MSEC:43case H_LONG_BUSY_ORDER_1_SEC:44case H_LONG_BUSY_ORDER_10_SEC:45case H_LONG_BUSY_ORDER_100_SEC:46return -EBUSY;47case H_FUNCTION:48default:49return -EPERM;50}51}5253/**54* hvcs_free_partner_info - free pi allocated by hvcs_get_partner_info55* @head: list_head pointer for an allocated list of partner info structs to56* free.57*58* This function is used to free the partner info list that was returned by59* calling hvcs_get_partner_info().60*/61int hvcs_free_partner_info(struct list_head *head)62{63struct hvcs_partner_info *pi;64struct list_head *element;6566if (!head)67return -EINVAL;6869while (!list_empty(head)) {70element = head->next;71pi = list_entry(element, struct hvcs_partner_info, node);72list_del(element);73kfree(pi);74}7576return 0;77}78EXPORT_SYMBOL(hvcs_free_partner_info);7980/* Helper function for hvcs_get_partner_info */81static int hvcs_next_partner(uint32_t unit_address,82unsigned long last_p_partition_ID,83unsigned long last_p_unit_address, unsigned long *pi_buff)8485{86long retval;87retval = plpar_hcall_norets(H_VTERM_PARTNER_INFO, unit_address,88last_p_partition_ID,89last_p_unit_address, virt_to_phys(pi_buff));90return hvcs_convert(retval);91}9293/**94* hvcs_get_partner_info - Get all of the partner info for a vty-server adapter95* @unit_address: The unit_address of the vty-server adapter for which this96* function is fetching partner info.97* @head: An initialized list_head pointer to an empty list to use to return the98* list of partner info fetched from the hypervisor to the caller.99* @pi_buff: A page sized buffer pre-allocated prior to calling this function100* that is to be used to be used by firmware as an iterator to keep track101* of the partner info retrieval.102*103* This function returns non-zero on success, or if there is no partner info.104*105* The pi_buff is pre-allocated prior to calling this function because this106* function may be called with a spin_lock held and kmalloc of a page is not107* recommended as GFP_ATOMIC.108*109* The first long of this buffer is used to store a partner unit address. The110* second long is used to store a partner partition ID and starting at111* pi_buff[2] is the 79 character Converged Location Code (diff size than the112* unsigned longs, hence the casting mumbo jumbo you see later).113*114* Invocation of this function should always be followed by an invocation of115* hvcs_free_partner_info() using a pointer to the SAME list head instance116* that was passed as a parameter to this function.117*/118int hvcs_get_partner_info(uint32_t unit_address, struct list_head *head,119unsigned long *pi_buff)120{121/*122* Dealt with as longs because of the hcall interface even though the123* values are uint32_t.124*/125unsigned long last_p_partition_ID;126unsigned long last_p_unit_address;127struct hvcs_partner_info *next_partner_info = NULL;128int more = 1;129int retval;130131/* invalid parameters */132if (!head || !pi_buff)133return -EINVAL;134135memset(pi_buff, 0x00, PAGE_SIZE);136last_p_partition_ID = last_p_unit_address = ~0UL;137INIT_LIST_HEAD(head);138139do {140retval = hvcs_next_partner(unit_address, last_p_partition_ID,141last_p_unit_address, pi_buff);142if (retval) {143/*144* Don't indicate that we've failed if we have145* any list elements.146*/147if (!list_empty(head))148return 0;149return retval;150}151152last_p_partition_ID = be64_to_cpu(pi_buff[0]);153last_p_unit_address = be64_to_cpu(pi_buff[1]);154155/* This indicates that there are no further partners */156if (last_p_partition_ID == ~0UL157&& last_p_unit_address == ~0UL)158break;159160/* This is a very small struct and will be freed soon in161* hvcs_free_partner_info(). */162next_partner_info = kmalloc(sizeof(struct hvcs_partner_info),163GFP_ATOMIC);164165if (!next_partner_info) {166printk(KERN_WARNING "HVCONSOLE: kmalloc() failed to"167" allocate partner info struct.\n");168hvcs_free_partner_info(head);169return -ENOMEM;170}171172next_partner_info->unit_address173= (unsigned int)last_p_unit_address;174next_partner_info->partition_ID175= (unsigned int)last_p_partition_ID;176177/* copy the Null-term char too */178strscpy(&next_partner_info->location_code[0],179(char *)&pi_buff[2],180sizeof(next_partner_info->location_code));181182list_add_tail(&(next_partner_info->node), head);183next_partner_info = NULL;184185} while (more);186187return 0;188}189EXPORT_SYMBOL(hvcs_get_partner_info);190191/**192* hvcs_register_connection - establish a connection between this vty-server and193* a vty.194* @unit_address: The unit address of the vty-server adapter that is to be195* establish a connection.196* @p_partition_ID: The partition ID of the vty adapter that is to be connected.197* @p_unit_address: The unit address of the vty adapter to which the vty-server198* is to be connected.199*200* If this function is called once and -EINVAL is returned it may201* indicate that the partner info needs to be refreshed for the202* target unit address at which point the caller must invoke203* hvcs_get_partner_info() and then call this function again. If,204* for a second time, -EINVAL is returned then it indicates that205* there is probably already a partner connection registered to a206* different vty-server adapter. It is also possible that a second207* -EINVAL may indicate that one of the parms is not valid, for208* instance if the link was removed between the vty-server adapter209* and the vty adapter that you are trying to open. Don't shoot the210* messenger. Firmware implemented it this way.211*/212int hvcs_register_connection( uint32_t unit_address,213uint32_t p_partition_ID, uint32_t p_unit_address)214{215long retval;216retval = plpar_hcall_norets(H_REGISTER_VTERM, unit_address,217p_partition_ID, p_unit_address);218return hvcs_convert(retval);219}220EXPORT_SYMBOL(hvcs_register_connection);221222/**223* hvcs_free_connection - free the connection between a vty-server and vty224* @unit_address: The unit address of the vty-server that is to have its225* connection severed.226*227* This function is used to free the partner connection between a vty-server228* adapter and a vty adapter.229*230* If -EBUSY is returned continue to call this function until 0 is returned.231*/232int hvcs_free_connection(uint32_t unit_address)233{234long retval;235retval = plpar_hcall_norets(H_FREE_VTERM, unit_address);236return hvcs_convert(retval);237}238EXPORT_SYMBOL(hvcs_free_connection);239240241