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