source: trunk/packages/xen-3.1/xen-3.1/xen/arch/powerpc/papr/xlate.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 16.5 KB
Line 
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
15 *
16 * Copyright (C) IBM Corp. 2005
17 *
18 * Authors: Jimi Xenidis <jimix@watson.ibm.com>
19 */
20
21#undef DEBUG
22#undef DEBUG_LOW
23
24#include <xen/config.h>
25#include <xen/types.h>
26#include <xen/sched.h>
27#include <xen/init.h>
28#include <public/xen.h>
29#include <asm/current.h>
30#include <asm/papr.h>
31#include <asm/hcalls.h>
32#include <asm/platform.h>
33
34#ifdef DEBUG
35#define DBG(fmt...) printk(fmt)
36#else
37#define DBG(fmt...)
38#endif
39#ifdef DEBUG_LOW
40#define DBG_LOW(fmt...) printk(fmt)
41#else
42#define DBG_LOW(fmt...)
43#endif
44
45#ifdef USE_PTE_INSERT
46static inline void pte_insert(union pte volatile *pte,
47        ulong vsid, ulong rpn, ulong lrpn)
48{
49    /*
50     * It's required that external locking be done to provide
51     * exclusion between the choices of insertion points.  Any valid
52     * choice of pte requires that the pte be invalid upon entry to
53     * this function.
54     */
55
56    ASSERT( (pte->bits.v == 0) );
57
58    /* Set shadow word. */
59    (void)lrpn;
60
61    /* Set the second word first so the valid bit is the last thing set */
62    pte->words.rpn = rpn;
63
64    /* Guarantee the second word is visible before the valid bit */
65    __asm__ __volatile__("eieio" : : : "memory");
66
67    /* Now set the first word including the valid bit */
68    pte->words.vsid = vsid;
69    /* Architecturally this instruction will cause a heavier operation
70     * if this one is not supported.  note: on come machines like Cell
71     * this coul dbe a nop */
72    __asm__ __volatile__("ptesync" : : : "memory");
73}
74#endif
75
76/*
77 * POWER Arch 2.03 Sec 4.12.1 (Yes 970 is one)
78 *
79 *   when a tlbsync instruction has been executed by a processor in a
80 *   given partition, a ptesync instruction must be executed by that
81 *   processor before a tlbie or tlbsync instruction is executed by
82 *   another processor in that partition.
83 *
84 * So for now, here is a BFLock to deal with it, the lock should be per-domain.
85 *
86 * XXX Will need to audit all tlb usege soon enough.
87 */
88
89static DEFINE_SPINLOCK(native_tlbie_lock);
90static void pte_tlbie(union pte volatile *pte, ulong ptex)
91{
92    ulong va;
93    ulong vsid;
94    ulong group;
95    ulong pi;
96    ulong pi_high;
97
98    vsid = pte->bits.avpn >> 5;
99    group = ptex >> 3;
100    if (pte->bits.h) {
101        group = ~group;
102    }
103    pi = (vsid ^ group) & 0x7ff;
104    pi_high = (pte->bits.avpn & 0x1f) << 11;
105    pi |= pi_high;
106    va = (pi << 12) | (vsid << 28);
107    va &= ~(0xffffULL << 48);
108
109    spin_lock(&native_tlbie_lock);
110#ifndef FLUSH_THE_WHOLE_THING
111    if (pte->bits.l) {
112        va |= (pte->bits.rpn & 1);
113        asm volatile("ptesync ;tlbie %0,1" : : "r"(va) : "memory");
114    } else {
115        asm volatile("ptesync; tlbie %0,0" : : "r"(va) : "memory");
116    }
117    asm volatile("eieio; tlbsync; ptesync" : : : "memory");
118#else
119    {
120        unsigned i;
121        ulong rb;
122
123        for (i = 0; i < 256; i++) {
124            rb = i;
125            rb <<= 12;
126            asm volatile("ptesync; tlbie %0,0; eieio; tlbsync; ptesync; isync"
127                    : "=r" (rb): : "memory");
128            asm volatile("ptesync; tlbie %0,1; eieio; tlbsync; ptesync; isync"
129                    : "=r" (rb): : "memory");
130        }
131    }
132#endif
133    spin_unlock(&native_tlbie_lock);
134}
135
136long pte_enter(ulong flags, ulong ptex, ulong vsid, ulong rpn)
137{
138    union pte pte;
139    union pte volatile *ppte;
140    struct domain_htab *htab;
141    int lp_bits = 0;
142    int pgshift = PAGE_SHIFT;
143    ulong idx;
144    int limit = 0;                /* how many PTEs to examine in the PTEG */
145    ulong pfn;
146    ulong mfn;
147    struct vcpu *v = get_current();
148    struct domain *d = v->domain;
149    int mtype;
150    struct page_info *pg = NULL;
151    struct domain *f = NULL;
152
153
154    htab = &d->arch.htab;
155    if (ptex > (1UL << htab->log_num_ptes)) {
156        DBG("%s: bad ptex: 0x%lx\n", __func__, ptex);
157        return H_Parameter;
158    }
159
160    /* use local HPTE to avoid manual shifting & masking */
161    pte.words.vsid = vsid;
162    pte.words.rpn = rpn;
163
164    if ( pte.bits.l ) {        /* large page? */
165        /* figure out the page size for the selected large page */
166        ulong lp_rpn = pte.bits.rpn;
167        uint lp_size = 0;
168
169        while ( lp_rpn & 0x1 ) {
170            lp_rpn >>= 1;
171            lp_bits = ((lp_bits << 1) | 0x1);
172            lp_size++;
173        }
174
175        if ( lp_size >= d->arch.large_page_sizes ) {
176            DBG("%s: attempt to use unsupported lp_size %d\n",
177                __func__, lp_size);
178            return H_Parameter;
179        }
180
181        /* get correct pgshift value */
182        pgshift = d->arch.large_page_order[lp_size] + PAGE_SHIFT;
183    }
184
185    /* get the correct logical RPN in terms of 4K pages need to mask
186     * off lp bits and unused arpn bits if this is a large page */
187
188    pfn = ~0ULL << (pgshift - PAGE_SHIFT);
189    pfn = pte.bits.rpn & pfn;
190
191    mfn = pfn2mfn(d, pfn, &mtype);
192    if (mfn == INVALID_MFN) {
193        DBG("%s: Bad PFN: 0x%lx\n", __func__, pfn);
194        return H_Parameter;
195    }
196
197    if (mtype == PFN_TYPE_IO && !d->is_privileged) {
198        /* only a privilaged dom can access outside IO space */
199        DBG("%s: unprivileged access to physical page: 0x%lx\n",
200            __func__, pfn);
201        return H_Privilege;
202    }
203    if (mtype == PFN_TYPE_IO) {
204        if ( !((pte.bits.w == 0)
205             && (pte.bits.i == 1)
206             && (pte.bits.g == 1)) ) {
207            DBG("%s: expecting an IO WIMG "
208                "w=%x i=%d m=%d, g=%d\n word 0x%lx\n", __func__,
209                pte.bits.w, pte.bits.i, pte.bits.m, pte.bits.g,
210                pte.words.rpn);
211            return H_Parameter;
212        }
213    }
214    if (mtype == PFN_TYPE_GNTTAB) {
215        DBG("%s: Dom[%d] mapping grant table: 0x%lx\n",
216            __func__, d->domain_id, pfn << PAGE_SHIFT);
217        pte.bits.i = 0;
218        pte.bits.g = 0;
219    }
220    /* fixup the RPN field of our local PTE copy */
221    pte.bits.rpn = mfn | lp_bits;
222
223    /* clear reserved bits in high word */
224    pte.bits.lock = 0x0;
225    pte.bits.res = 0x0;
226
227    /* clear reserved bits in low word */
228    pte.bits.pp0 = 0x0;
229    pte.bits.ts = 0x0;
230    pte.bits.res2 = 0x0;
231
232    if (mtype == PFN_TYPE_FOREIGN) {
233        pg = mfn_to_page(mfn);
234        f = page_get_owner(pg);
235       
236        BUG_ON(f == d);
237
238        if (unlikely(!get_domain(f))) {
239            DBG("%s: Rescinded, no domain: 0x%lx\n",  __func__, pfn);
240            return H_Rescinded;
241        }
242        if (unlikely(!get_page(pg, f))) {
243            put_domain(f);
244            DBG("%s: Rescinded, no page: 0x%lx\n",  __func__, pfn);
245            return H_Rescinded;
246        }
247    }
248
249    if ( !(flags & H_EXACT) ) {
250        /* PTEG (not specific PTE); clear 3 lowest bits */
251        ptex &= ~0x7UL;
252        limit = 7;
253    }
254
255    /* data manipulations should be done prior to the pte insertion. */
256    if ( flags & H_ZERO_PAGE ) {
257        ulong pg = mfn << PAGE_SHIFT;
258        ulong pgs = 1UL << pgshift;
259
260        while (pgs > 0) {
261            clear_page((void *)pg);
262            pg += PAGE_SIZE;
263            --pgs;
264        }
265    }
266
267    if ( flags & H_ICACHE_INVALIDATE ) {
268        ulong k;
269        ulong addr = mfn << PAGE_SHIFT;
270
271        for (k = 0; k < (1UL << pgshift); k += L1_CACHE_BYTES) {
272            dcbst(addr + k);
273            sync();
274            icbi(addr + k);
275            sync();
276            isync();
277        }
278    }
279
280    if ( flags & H_ICACHE_SYNCHRONIZE ) {
281        ulong k;
282        ulong addr = mfn << PAGE_SHIFT;
283        for (k = 0; k < (1UL << pgshift); k += L1_CACHE_BYTES) {
284            icbi(addr + k);
285            sync();
286            isync();
287        }
288    }
289
290    for (idx = ptex; idx <= ptex + limit; idx++) {
291        ppte = &htab->map[idx];
292
293        if ( ppte->bits.v == 0 && ppte->bits.lock == 0) {
294            /* got it */
295
296            asm volatile(
297                "std %1, 8(%0); eieio; std %2, 0(%0); ptesync"
298                : 
299                : "b" (ppte), "r" (pte.words.rpn), "r" (pte.words.vsid)
300                : "memory");
301
302            return idx;
303        }
304    }
305
306    /* If the PTEG is full then no additional values are returned. */
307    DBG("%s: PTEG FULL\n", __func__);
308
309    if (pg != NULL)
310        put_page(pg);
311
312    if (f != NULL)
313        put_domain(f);
314
315    return H_PTEG_Full;
316}
317
318static void h_enter(struct cpu_user_regs *regs)
319{
320    ulong flags = regs->gprs[4];
321    ulong ptex = regs->gprs[5];
322    ulong vsid = regs->gprs[6];
323    ulong rpn = regs->gprs[7];
324    long ret;
325
326    ret = pte_enter(flags, ptex, vsid, rpn);
327
328    if (ret >= 0) {
329        regs->gprs[3] = H_Success;
330        regs->gprs[4] = ret;
331    } else
332        regs->gprs[3] = ret;
333}
334
335static void h_protect(struct cpu_user_regs *regs)
336{
337    ulong flags = regs->gprs[4];
338    ulong ptex = regs->gprs[5];
339    ulong avpn = regs->gprs[6];
340    struct vcpu *v = get_current();
341    struct domain *d = v->domain;
342    struct domain_htab *htab = &d->arch.htab;
343    union pte volatile *ppte;
344    union pte lpte;
345
346    DBG_LOW("%s: flags: 0x%lx ptex: 0x%lx avpn: 0x%lx\n", __func__,
347            flags, ptex, avpn);
348    if ( ptex > (1UL << htab->log_num_ptes) ) {
349        DBG("%s: bad ptex: 0x%lx\n", __func__, ptex);
350        regs->gprs[3] = H_Parameter;
351        return;
352    }
353    ppte = &htab->map[ptex];
354
355    lpte.words.vsid = ppte->words.vsid;
356    lpte.words.rpn = ppte->words.rpn;
357
358    /* the AVPN param occupies the bit-space of the word */
359    if ( (flags & H_AVPN) && lpte.bits.avpn != avpn >> 7 ) {
360        DBG_LOW("%s: %p: AVPN check failed: 0x%lx, 0x%lx\n", __func__,
361            ppte, lpte.words.vsid, lpte.words.rpn);
362        regs->gprs[3] = H_Not_Found;
363        return;
364    }
365
366    if (lpte.bits.v == 0) {
367        /* the PAPR does not specify what to do here, this is because
368         * we invalidate entires where the PAPR says to 0 the whole hi
369         * dword, so the AVPN should catch this first */
370
371        DBG("%s: pte invalid\n", __func__);
372        regs->gprs[3] =  H_Not_Found;
373        return;
374    }
375
376    lpte.bits.v = 0;
377   
378    /* ppte->words.vsid = lpte.words.vsid; */
379    asm volatile(
380        "eieio; std %1, 0(%0); ptesync"
381        : 
382        : "b" (ppte), "r" (0)
383        : "memory");
384
385    pte_tlbie(&lpte, ptex);
386
387    /* We never touch pp0, and PP bits in flags are in the right
388     * order */
389    lpte.bits.pp1 = flags & (H_PP1 | H_PP2);
390    lpte.bits.n = (flags & H_N) ? 1 : 0;
391
392    lpte.bits.v = 1;
393    lpte.bits.r = 0;
394
395    asm volatile(
396        "std  %1, 8(%0); eieio; std %2, 0(%0); ptesync"
397        : 
398        : "b" (ppte), "r" (lpte.words.rpn), "r" (lpte.words.vsid)
399        : "memory");
400
401    regs->gprs[3] = H_Success;
402}
403
404static void h_clear_ref(struct cpu_user_regs *regs)
405{
406    ulong ptex = regs->gprs[5];
407    struct vcpu *v = get_current();
408    struct domain *d = v->domain;
409    struct domain_htab *htab = &d->arch.htab;
410    union pte volatile *pte;
411    union pte lpte;
412
413    DBG_LOW("%s: flags: 0x%lx ptex: 0x%lx\n", __func__,
414            regs->gprs[4], ptex);
415
416#ifdef DEBUG
417    if (regs->gprs[4] != 0) {
418        DBG("WARNING: %s: "
419            "flags are undefined and should be 0: 0x%lx\n",
420            __func__, regs->gprs[4]);
421    }
422#endif
423
424    if (ptex > (1UL << htab->log_num_ptes)) {
425        DBG("%s: bad ptex: 0x%lx\n", __func__, ptex);
426        regs->gprs[3] = H_Parameter;
427        return;
428    }
429    pte = &htab->map[ptex];
430    lpte.words.rpn = pte->words.rpn;
431
432    regs->gprs[4] = lpte.words.rpn;
433
434    if (lpte.bits.r != 0) {
435        lpte.bits.r = 0;
436
437        asm volatile("std  %1, 8(%0); eieio; ptesync"
438                : 
439                : "b" (pte), "r" (lpte.words.rpn) : "memory");
440
441        pte_tlbie(&lpte, ptex);
442    }
443    regs->gprs[3] = H_Success;
444}
445
446static void h_clear_mod(struct cpu_user_regs *regs)
447{
448    ulong ptex = regs->gprs[5];
449    struct vcpu *v = get_current();
450    struct domain *d = v->domain;
451    struct domain_htab *htab = &d->arch.htab;
452    union pte volatile *pte;
453    union pte lpte;
454
455    DBG_LOW("%s: flags: 0x%lx ptex: 0x%lx\n", __func__,
456          regs->gprs[4], ptex);
457
458#ifdef DEBUG
459    if (regs->gprs[4] != 0) {
460        DBG("WARNING: %s: "
461            "flags are undefined and should be 0: 0x%lx\n",
462            __func__, regs->gprs[4]);
463    }
464#endif
465
466    if (ptex > (1UL << htab->log_num_ptes)) {
467        DBG("%s: bad ptex: 0x%lx\n", __func__, ptex);
468        regs->gprs[3] = H_Parameter;
469        return;
470    }
471    pte = &htab->map[ptex];
472    lpte.words.vsid = pte->words.vsid;
473    lpte.words.rpn = pte->words.rpn;
474
475    regs->gprs[3] = H_Success;
476    regs->gprs[4] = lpte.words.rpn;
477
478    if (lpte.bits.c != 0) {
479        /* invalidate */
480        asm volatile(
481                "eieio; std %1, 0(%0); ptesync"
482                : 
483                : "b" (pte), "r" (0)
484                : "memory");
485
486        pte_tlbie(&lpte, ptex);
487
488        lpte.bits.c = 0;
489        asm volatile(
490                "std  %1, 8(%0); eieio; std %2, 0(%0); ptesync"
491                : 
492                : "b" (pte), "r" (lpte.words.rpn), "r" (lpte.words.vsid)
493                : "memory");
494    }
495}
496
497long pte_remove(ulong flags, ulong ptex, ulong avpn, ulong *hi, ulong *lo)
498{
499    struct vcpu *v = get_current();
500    struct domain *d = v->domain;
501    struct domain_htab *htab = &d->arch.htab;
502    union pte volatile *pte;
503    union pte lpte;
504
505    DBG_LOW("%s: flags: 0x%lx ptex: 0x%lx avpn: 0x%lx\n", __func__,
506            flags, ptex, avpn);
507
508    if ( ptex > (1UL << htab->log_num_ptes) ) {
509        DBG("%s: bad ptex: 0x%lx\n", __func__, ptex);
510        return H_Parameter;
511    }
512    pte = &htab->map[ptex];
513    lpte.words.vsid = pte->words.vsid;
514    lpte.words.rpn = pte->words.rpn;
515
516    if ((flags & H_AVPN) && lpte.bits.avpn != (avpn >> 7)) {
517        DBG_LOW("%s: AVPN does not match\n", __func__);
518        return H_Not_Found;
519    }
520
521    if ((flags & H_ANDCOND) && ((avpn & pte->words.vsid) != 0)) {
522        DBG("%s: andcond does not match\n", __func__);
523        return H_Not_Found;
524    }
525
526    /* return old PTE in regs 4 and 5 */
527    *hi = lpte.words.vsid;
528    *lo = lpte.words.rpn;
529
530#ifdef DEBUG_LOW
531    /* XXX - I'm very skeptical of doing ANYTHING if not bits.v */
532    /* XXX - I think the spec should be questioned in this case (MFM) */
533    if (lpte.bits.v == 0) {
534        DBG_LOW("%s: removing invalid entry\n", __func__);
535    }
536#endif
537
538    if (lpte.bits.v) {
539        ulong mfn = lpte.bits.rpn;
540        if (!platform_io_mfn(mfn)) {
541            struct page_info *pg = mfn_to_page(mfn);
542            struct domain *f = page_get_owner(pg);
543           
544            if (f != d) {
545                put_domain(f);
546                put_page(pg);
547            }
548        }
549    }
550
551    asm volatile("eieio; std %1, 0(%0); ptesync"
552            :
553            : "b" (pte), "r" (0)
554            : "memory");
555
556    pte_tlbie(&lpte, ptex);
557
558    return H_Success;
559}
560
561static void h_remove(struct cpu_user_regs *regs)
562{
563    ulong flags = regs->gprs[4];
564    ulong ptex = regs->gprs[5];
565    ulong avpn = regs->gprs[6];
566    ulong hi, lo;
567    long ret;
568
569    ret = pte_remove(flags, ptex, avpn, &hi, &lo);
570
571    regs->gprs[3] = ret;
572
573    if (ret == H_Success) {
574        regs->gprs[4] = hi;
575        regs->gprs[5] = lo;
576    }
577    return;
578}
579
580static void h_read(struct cpu_user_regs *regs)
581{
582    ulong flags = regs->gprs[4];
583    ulong ptex = regs->gprs[5];
584    struct vcpu *v = get_current();
585    struct domain *d = v->domain;
586    struct domain_htab *htab = &d->arch.htab;
587    union pte volatile *pte;
588
589    if (flags & H_READ_4)
590        ptex &= ~0x3UL;
591
592    if (ptex > (1UL << htab->log_num_ptes)) {
593        DBG("%s: bad ptex: 0x%lx\n", __func__, ptex);
594        regs->gprs[3] = H_Parameter;
595        return;
596    }
597    pte = &htab->map[ptex];
598    regs->gprs[4] = pte[0].words.vsid;
599    regs->gprs[5] = pte[0].words.rpn;
600
601    if (!(flags & H_READ_4)) {
602        /* dump another 3 PTEs */
603        regs->gprs[6] = pte[1].words.vsid;
604        regs->gprs[7] = pte[1].words.rpn;
605        regs->gprs[8] = pte[2].words.vsid;
606        regs->gprs[9] = pte[2].words.rpn;
607        regs->gprs[10] = pte[3].words.vsid;
608        regs->gprs[11] = pte[3].words.rpn;
609    }
610
611    regs->gprs[3] = H_Success;
612}
613
614__init_papr_hcall(H_ENTER, h_enter);
615__init_papr_hcall(H_READ, h_read);
616__init_papr_hcall(H_REMOVE, h_remove);
617__init_papr_hcall(H_CLEAR_MOD, h_clear_mod);
618__init_papr_hcall(H_CLEAR_REF, h_clear_ref);
619__init_papr_hcall(H_PROTECT, h_protect);
Note: See TracBrowser for help on using the repository browser.