| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * hvcserver.c | 
 | 3 |  * Copyright (C) 2004 Ryan S Arnold, IBM Corporation | 
 | 4 |  * | 
 | 5 |  * PPC64 virtual I/O console server support. | 
 | 6 |  * | 
 | 7 |  * This program is free software; you can redistribute it and/or modify | 
 | 8 |  * it under the terms of the GNU General Public License as published by | 
 | 9 |  * the Free Software Foundation; either version 2 of the License, or | 
 | 10 |  * (at your option) any later version. | 
 | 11 |  * | 
 | 12 |  * This program is distributed in the hope that it will be useful, | 
 | 13 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 14 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 15 |  * GNU General Public License for more details. | 
 | 16 |  * | 
 | 17 |  * You should have received a copy of the GNU General Public License | 
 | 18 |  * along with this program; if not, write to the Free Software | 
 | 19 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
 | 20 |  */ | 
 | 21 |  | 
 | 22 | #include <linux/kernel.h> | 
 | 23 | #include <linux/list.h> | 
 | 24 | #include <linux/module.h> | 
 | 25 | #include <asm/hvcall.h> | 
 | 26 | #include <asm/hvcserver.h> | 
 | 27 | #include <asm/io.h> | 
 | 28 |  | 
 | 29 | #define HVCS_ARCH_VERSION "1.0.0" | 
 | 30 |  | 
 | 31 | MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>"); | 
 | 32 | MODULE_DESCRIPTION("IBM hvcs ppc64 API"); | 
 | 33 | MODULE_LICENSE("GPL"); | 
 | 34 | MODULE_VERSION(HVCS_ARCH_VERSION); | 
 | 35 |  | 
 | 36 | /* | 
 | 37 |  * Convert arch specific return codes into relevant errnos.  The hvcs | 
 | 38 |  * functions aren't performance sensitive, so this conversion isn't an | 
 | 39 |  * issue. | 
 | 40 |  */ | 
 | 41 | int hvcs_convert(long to_convert) | 
 | 42 | { | 
 | 43 | 	switch (to_convert) { | 
 | 44 | 		case H_Success: | 
 | 45 | 			return 0; | 
 | 46 | 		case H_Parameter: | 
 | 47 | 			return -EINVAL; | 
 | 48 | 		case H_Hardware: | 
 | 49 | 			return -EIO; | 
 | 50 | 		case H_Busy: | 
 | 51 | 		case H_LongBusyOrder1msec: | 
 | 52 | 		case H_LongBusyOrder10msec: | 
 | 53 | 		case H_LongBusyOrder100msec: | 
 | 54 | 		case H_LongBusyOrder1sec: | 
 | 55 | 		case H_LongBusyOrder10sec: | 
 | 56 | 		case H_LongBusyOrder100sec: | 
 | 57 | 			return -EBUSY; | 
 | 58 | 		case H_Function: /* fall through */ | 
 | 59 | 		default: | 
 | 60 | 			return -EPERM; | 
 | 61 | 	} | 
 | 62 | } | 
 | 63 |  | 
 | 64 | /** | 
 | 65 |  * hvcs_free_partner_info - free pi allocated by hvcs_get_partner_info | 
 | 66 |  * @head: list_head pointer for an allocated list of partner info structs to | 
 | 67 |  *	free. | 
 | 68 |  * | 
 | 69 |  * This function is used to free the partner info list that was returned by | 
 | 70 |  * calling hvcs_get_partner_info(). | 
 | 71 |  */ | 
 | 72 | int hvcs_free_partner_info(struct list_head *head) | 
 | 73 | { | 
 | 74 | 	struct hvcs_partner_info *pi; | 
 | 75 | 	struct list_head *element; | 
 | 76 |  | 
 | 77 | 	if (!head) | 
 | 78 | 		return -EINVAL; | 
 | 79 |  | 
 | 80 | 	while (!list_empty(head)) { | 
 | 81 | 		element = head->next; | 
 | 82 | 		pi = list_entry(element, struct hvcs_partner_info, node); | 
 | 83 | 		list_del(element); | 
 | 84 | 		kfree(pi); | 
 | 85 | 	} | 
 | 86 |  | 
 | 87 | 	return 0; | 
 | 88 | } | 
 | 89 | EXPORT_SYMBOL(hvcs_free_partner_info); | 
 | 90 |  | 
 | 91 | /* Helper function for hvcs_get_partner_info */ | 
 | 92 | int hvcs_next_partner(uint32_t unit_address, | 
 | 93 | 		unsigned long last_p_partition_ID, | 
 | 94 | 		unsigned long last_p_unit_address, unsigned long *pi_buff) | 
 | 95 |  | 
 | 96 | { | 
 | 97 | 	long retval; | 
 | 98 | 	retval = plpar_hcall_norets(H_VTERM_PARTNER_INFO, unit_address, | 
 | 99 | 			last_p_partition_ID, | 
 | 100 | 				last_p_unit_address, virt_to_phys(pi_buff)); | 
 | 101 | 	return hvcs_convert(retval); | 
 | 102 | } | 
 | 103 |  | 
 | 104 | /** | 
 | 105 |  * hvcs_get_partner_info - Get all of the partner info for a vty-server adapter | 
 | 106 |  * @unit_address: The unit_address of the vty-server adapter for which this | 
 | 107 |  *	function is fetching partner info. | 
 | 108 |  * @head: An initialized list_head pointer to an empty list to use to return the | 
 | 109 |  *	list of partner info fetched from the hypervisor to the caller. | 
 | 110 |  * @pi_buff: A page sized buffer pre-allocated prior to calling this function | 
 | 111 |  *	that is to be used to be used by firmware as an iterator to keep track | 
 | 112 |  *	of the partner info retrieval. | 
 | 113 |  * | 
 | 114 |  * This function returns non-zero on success, or if there is no partner info. | 
 | 115 |  * | 
 | 116 |  * The pi_buff is pre-allocated prior to calling this function because this | 
 | 117 |  * function may be called with a spin_lock held and kmalloc of a page is not | 
 | 118 |  * recommended as GFP_ATOMIC. | 
 | 119 |  * | 
 | 120 |  * The first long of this buffer is used to store a partner unit address.  The | 
 | 121 |  * second long is used to store a partner partition ID and starting at | 
 | 122 |  * pi_buff[2] is the 79 character Converged Location Code (diff size than the | 
 | 123 |  * unsigned longs, hence the casting mumbo jumbo you see later). | 
 | 124 |  * | 
 | 125 |  * Invocation of this function should always be followed by an invocation of | 
 | 126 |  * hvcs_free_partner_info() using a pointer to the SAME list head instance | 
 | 127 |  * that was passed as a parameter to this function. | 
 | 128 |  */ | 
 | 129 | int hvcs_get_partner_info(uint32_t unit_address, struct list_head *head, | 
 | 130 | 		unsigned long *pi_buff) | 
 | 131 | { | 
 | 132 | 	/* | 
 | 133 | 	 * Dealt with as longs because of the hcall interface even though the | 
 | 134 | 	 * values are uint32_t. | 
 | 135 | 	 */ | 
 | 136 | 	unsigned long	last_p_partition_ID; | 
 | 137 | 	unsigned long	last_p_unit_address; | 
 | 138 | 	struct hvcs_partner_info *next_partner_info = NULL; | 
 | 139 | 	int more = 1; | 
 | 140 | 	int retval; | 
 | 141 |  | 
 | 142 | 	memset(pi_buff, 0x00, PAGE_SIZE); | 
 | 143 | 	/* invalid parameters */ | 
 | 144 | 	if (!head || !pi_buff) | 
 | 145 | 		return -EINVAL; | 
 | 146 |  | 
 | 147 | 	last_p_partition_ID = last_p_unit_address = ~0UL; | 
 | 148 | 	INIT_LIST_HEAD(head); | 
 | 149 |  | 
 | 150 | 	do { | 
 | 151 | 		retval = hvcs_next_partner(unit_address, last_p_partition_ID, | 
 | 152 | 				last_p_unit_address, pi_buff); | 
 | 153 | 		if (retval) { | 
 | 154 | 			/* | 
 | 155 | 			 * Don't indicate that we've failed if we have | 
 | 156 | 			 * any list elements. | 
 | 157 | 			 */ | 
 | 158 | 			if (!list_empty(head)) | 
 | 159 | 				return 0; | 
 | 160 | 			return retval; | 
 | 161 | 		} | 
 | 162 |  | 
 | 163 | 		last_p_partition_ID = pi_buff[0]; | 
 | 164 | 		last_p_unit_address = pi_buff[1]; | 
 | 165 |  | 
 | 166 | 		/* This indicates that there are no further partners */ | 
 | 167 | 		if (last_p_partition_ID == ~0UL | 
 | 168 | 				&& last_p_unit_address == ~0UL) | 
 | 169 | 			break; | 
 | 170 |  | 
 | 171 | 		/* This is a very small struct and will be freed soon in | 
 | 172 | 		 * hvcs_free_partner_info(). */ | 
 | 173 | 		next_partner_info = kmalloc(sizeof(struct hvcs_partner_info), | 
 | 174 | 				GFP_ATOMIC); | 
 | 175 |  | 
 | 176 | 		if (!next_partner_info) { | 
 | 177 | 			printk(KERN_WARNING "HVCONSOLE: kmalloc() failed to" | 
 | 178 | 				" allocate partner info struct.\n"); | 
 | 179 | 			hvcs_free_partner_info(head); | 
 | 180 | 			return -ENOMEM; | 
 | 181 | 		} | 
 | 182 |  | 
 | 183 | 		next_partner_info->unit_address | 
 | 184 | 			= (unsigned int)last_p_unit_address; | 
 | 185 | 		next_partner_info->partition_ID | 
 | 186 | 			= (unsigned int)last_p_partition_ID; | 
 | 187 |  | 
 | 188 | 		/* copy the Null-term char too */ | 
 | 189 | 		strncpy(&next_partner_info->location_code[0], | 
 | 190 | 			(char *)&pi_buff[2], | 
 | 191 | 			strlen((char *)&pi_buff[2]) + 1); | 
 | 192 |  | 
 | 193 | 		list_add_tail(&(next_partner_info->node), head); | 
 | 194 | 		next_partner_info = NULL; | 
 | 195 |  | 
 | 196 | 	} while (more); | 
 | 197 |  | 
 | 198 | 	return 0; | 
 | 199 | } | 
 | 200 | EXPORT_SYMBOL(hvcs_get_partner_info); | 
 | 201 |  | 
 | 202 | /** | 
 | 203 |  * hvcs_register_connection - establish a connection between this vty-server and | 
 | 204 |  *	a vty. | 
 | 205 |  * @unit_address: The unit address of the vty-server adapter that is to be | 
 | 206 |  *	establish a connection. | 
 | 207 |  * @p_partition_ID: The partition ID of the vty adapter that is to be connected. | 
 | 208 |  * @p_unit_address: The unit address of the vty adapter to which the vty-server | 
 | 209 |  *	is to be connected. | 
 | 210 |  * | 
 | 211 |  * If this function is called once and -EINVAL is returned it may | 
 | 212 |  * indicate that the partner info needs to be refreshed for the | 
 | 213 |  * target unit address at which point the caller must invoke | 
 | 214 |  * hvcs_get_partner_info() and then call this function again.  If, | 
 | 215 |  * for a second time, -EINVAL is returned then it indicates that | 
 | 216 |  * there is probably already a partner connection registered to a | 
 | 217 |  * different vty-server adapter.  It is also possible that a second | 
 | 218 |  * -EINVAL may indicate that one of the parms is not valid, for | 
 | 219 |  * instance if the link was removed between the vty-server adapter | 
 | 220 |  * and the vty adapter that you are trying to open.  Don't shoot the | 
 | 221 |  * messenger.  Firmware implemented it this way. | 
 | 222 |  */ | 
 | 223 | int hvcs_register_connection( uint32_t unit_address, | 
 | 224 | 		uint32_t p_partition_ID, uint32_t p_unit_address) | 
 | 225 | { | 
 | 226 | 	long retval; | 
 | 227 | 	retval = plpar_hcall_norets(H_REGISTER_VTERM, unit_address, | 
 | 228 | 				p_partition_ID, p_unit_address); | 
 | 229 | 	return hvcs_convert(retval); | 
 | 230 | } | 
 | 231 | EXPORT_SYMBOL(hvcs_register_connection); | 
 | 232 |  | 
 | 233 | /** | 
 | 234 |  * hvcs_free_connection - free the connection between a vty-server and vty | 
 | 235 |  * @unit_address: The unit address of the vty-server that is to have its | 
 | 236 |  *	connection severed. | 
 | 237 |  * | 
 | 238 |  * This function is used to free the partner connection between a vty-server | 
 | 239 |  * adapter and a vty adapter. | 
 | 240 |  * | 
 | 241 |  * If -EBUSY is returned continue to call this function until 0 is returned. | 
 | 242 |  */ | 
 | 243 | int hvcs_free_connection(uint32_t unit_address) | 
 | 244 | { | 
 | 245 | 	long retval; | 
 | 246 | 	retval = plpar_hcall_norets(H_FREE_VTERM, unit_address); | 
 | 247 | 	return hvcs_convert(retval); | 
 | 248 | } | 
 | 249 | EXPORT_SYMBOL(hvcs_free_connection); |