Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/platforms/iseries/htab.c
10820 views
1
/*
2
* iSeries hashtable management.
3
* Derived from pSeries_htab.c
4
*
5
* SMP scalability work:
6
* Copyright (C) 2001 Anton Blanchard <[email protected]>, IBM
7
*
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public License
10
* as published by the Free Software Foundation; either version
11
* 2 of the License, or (at your option) any later version.
12
*/
13
#include <asm/machdep.h>
14
#include <asm/pgtable.h>
15
#include <asm/mmu.h>
16
#include <asm/mmu_context.h>
17
#include <asm/abs_addr.h>
18
#include <linux/spinlock.h>
19
20
#include "call_hpt.h"
21
22
static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp;
23
24
/*
25
* Very primitive algorithm for picking up a lock
26
*/
27
static inline void iSeries_hlock(unsigned long slot)
28
{
29
if (slot & 0x8)
30
slot = ~slot;
31
spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
32
}
33
34
static inline void iSeries_hunlock(unsigned long slot)
35
{
36
if (slot & 0x8)
37
slot = ~slot;
38
spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
39
}
40
41
static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va,
42
unsigned long pa, unsigned long rflags,
43
unsigned long vflags, int psize, int ssize)
44
{
45
long slot;
46
struct hash_pte lhpte;
47
int secondary = 0;
48
49
BUG_ON(psize != MMU_PAGE_4K);
50
51
/*
52
* The hypervisor tries both primary and secondary.
53
* If we are being called to insert in the secondary,
54
* it means we have already tried both primary and secondary,
55
* so we return failure immediately.
56
*/
57
if (vflags & HPTE_V_SECONDARY)
58
return -1;
59
60
iSeries_hlock(hpte_group);
61
62
slot = HvCallHpt_findValid(&lhpte, va >> HW_PAGE_SHIFT);
63
if (unlikely(lhpte.v & HPTE_V_VALID)) {
64
if (vflags & HPTE_V_BOLTED) {
65
HvCallHpt_setSwBits(slot, 0x10, 0);
66
HvCallHpt_setPp(slot, PP_RWXX);
67
iSeries_hunlock(hpte_group);
68
if (slot < 0)
69
return 0x8 | (slot & 7);
70
else
71
return slot & 7;
72
}
73
BUG();
74
}
75
76
if (slot == -1) { /* No available entry found in either group */
77
iSeries_hunlock(hpte_group);
78
return -1;
79
}
80
81
if (slot < 0) { /* MSB set means secondary group */
82
vflags |= HPTE_V_SECONDARY;
83
secondary = 1;
84
slot &= 0x7fffffffffffffff;
85
}
86
87
88
lhpte.v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M) |
89
vflags | HPTE_V_VALID;
90
lhpte.r = hpte_encode_r(phys_to_abs(pa), MMU_PAGE_4K) | rflags;
91
92
/* Now fill in the actual HPTE */
93
HvCallHpt_addValidate(slot, secondary, &lhpte);
94
95
iSeries_hunlock(hpte_group);
96
97
return (secondary << 3) | (slot & 7);
98
}
99
100
static unsigned long iSeries_hpte_getword0(unsigned long slot)
101
{
102
struct hash_pte hpte;
103
104
HvCallHpt_get(&hpte, slot);
105
return hpte.v;
106
}
107
108
static long iSeries_hpte_remove(unsigned long hpte_group)
109
{
110
unsigned long slot_offset;
111
int i;
112
unsigned long hpte_v;
113
114
/* Pick a random slot to start at */
115
slot_offset = mftb() & 0x7;
116
117
iSeries_hlock(hpte_group);
118
119
for (i = 0; i < HPTES_PER_GROUP; i++) {
120
hpte_v = iSeries_hpte_getword0(hpte_group + slot_offset);
121
122
if (! (hpte_v & HPTE_V_BOLTED)) {
123
HvCallHpt_invalidateSetSwBitsGet(hpte_group +
124
slot_offset, 0, 0);
125
iSeries_hunlock(hpte_group);
126
return i;
127
}
128
129
slot_offset++;
130
slot_offset &= 0x7;
131
}
132
133
iSeries_hunlock(hpte_group);
134
135
return -1;
136
}
137
138
/*
139
* The HyperVisor expects the "flags" argument in this form:
140
* bits 0..59 : reserved
141
* bit 60 : N
142
* bits 61..63 : PP2,PP1,PP0
143
*/
144
static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp,
145
unsigned long va, int psize, int ssize, int local)
146
{
147
struct hash_pte hpte;
148
unsigned long want_v;
149
150
iSeries_hlock(slot);
151
152
HvCallHpt_get(&hpte, slot);
153
want_v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M);
154
155
if (HPTE_V_COMPARE(hpte.v, want_v) && (hpte.v & HPTE_V_VALID)) {
156
/*
157
* Hypervisor expects bits as NPPP, which is
158
* different from how they are mapped in our PP.
159
*/
160
HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1));
161
iSeries_hunlock(slot);
162
return 0;
163
}
164
iSeries_hunlock(slot);
165
166
return -1;
167
}
168
169
/*
170
* Functions used to find the PTE for a particular virtual address.
171
* Only used during boot when bolting pages.
172
*
173
* Input : vpn : virtual page number
174
* Output: PTE index within the page table of the entry
175
* -1 on failure
176
*/
177
static long iSeries_hpte_find(unsigned long vpn)
178
{
179
struct hash_pte hpte;
180
long slot;
181
182
/*
183
* The HvCallHpt_findValid interface is as follows:
184
* 0xffffffffffffffff : No entry found.
185
* 0x00000000xxxxxxxx : Entry found in primary group, slot x
186
* 0x80000000xxxxxxxx : Entry found in secondary group, slot x
187
*/
188
slot = HvCallHpt_findValid(&hpte, vpn);
189
if (hpte.v & HPTE_V_VALID) {
190
if (slot < 0) {
191
slot &= 0x7fffffffffffffff;
192
slot = -slot;
193
}
194
} else
195
slot = -1;
196
return slot;
197
}
198
199
/*
200
* Update the page protection bits. Intended to be used to create
201
* guard pages for kernel data structures on pages which are bolted
202
* in the HPT. Assumes pages being operated on will not be stolen.
203
* Does not work on large pages.
204
*
205
* No need to lock here because we should be the only user.
206
*/
207
static void iSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
208
int psize, int ssize)
209
{
210
unsigned long vsid,va,vpn;
211
long slot;
212
213
BUG_ON(psize != MMU_PAGE_4K);
214
215
vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M);
216
va = (vsid << 28) | (ea & 0x0fffffff);
217
vpn = va >> HW_PAGE_SHIFT;
218
slot = iSeries_hpte_find(vpn);
219
if (slot == -1)
220
panic("updateboltedpp: Could not find page to bolt\n");
221
HvCallHpt_setPp(slot, newpp);
222
}
223
224
static void iSeries_hpte_invalidate(unsigned long slot, unsigned long va,
225
int psize, int ssize, int local)
226
{
227
unsigned long hpte_v;
228
unsigned long avpn = va >> 23;
229
unsigned long flags;
230
231
local_irq_save(flags);
232
233
iSeries_hlock(slot);
234
235
hpte_v = iSeries_hpte_getword0(slot);
236
237
if ((HPTE_V_AVPN_VAL(hpte_v) == avpn) && (hpte_v & HPTE_V_VALID))
238
HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0);
239
240
iSeries_hunlock(slot);
241
242
local_irq_restore(flags);
243
}
244
245
void __init hpte_init_iSeries(void)
246
{
247
int i;
248
249
for (i = 0; i < ARRAY_SIZE(iSeries_hlocks); i++)
250
spin_lock_init(&iSeries_hlocks[i]);
251
252
ppc_md.hpte_invalidate = iSeries_hpte_invalidate;
253
ppc_md.hpte_updatepp = iSeries_hpte_updatepp;
254
ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp;
255
ppc_md.hpte_insert = iSeries_hpte_insert;
256
ppc_md.hpte_remove = iSeries_hpte_remove;
257
}
258
259