/[gxemul]/trunk/src/cpus/memory_ppc.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/src/cpus/memory_ppc.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 18 by dpavlin, Mon Oct 8 16:19:11 2007 UTC revision 26 by dpavlin, Mon Oct 8 16:20:10 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: memory_ppc.c,v 1.4 2005/10/27 14:01:13 debug Exp $   *  $Id: memory_ppc.c,v 1.24 2006/06/24 21:47:23 debug Exp $
29   *   *
30   *  Included from cpu_ppc.c.   *  Included from cpu_ppc.c.
31   */   */
32    
33    
34    #include "ppc_bat.h"
35    #include "ppc_pte.h"
36    
37    
38    /*
39     *  ppc_bat():
40     *
41     *  BAT translation. Returns -1 if there was no BAT hit, >= 0 for a hit.
42     *  (0 for access denied, 1 for read-only, and 2 for read-write access allowed.)
43     */
44    int ppc_bat(struct cpu *cpu, uint64_t vaddr, uint64_t *return_paddr, int flags,
45            int user)
46    {
47            int i, istart = 0, iend = 8, pp;
48    
49            if (flags & FLAG_INSTR)
50                    iend = 4;
51            else
52                    istart = 4;
53    
54            if (cpu->cd.ppc.bits != 32) {
55                    fatal("TODO: ppc_bat() for non-32-bit\n");
56                    exit(1);
57            }
58            if (cpu->cd.ppc.cpu_type.flags & PPC_601) {
59                    fatal("TODO: ppc_bat() for PPC 601\n");
60                    exit(1);
61            }
62    
63            /*  Scan either the 4 instruction BATs or the 4 data BATs:  */
64            for (i=istart; i<iend; i++) {
65                    int regnr = SPR_IBAT0U + i * 2;
66                    uint32_t upper = cpu->cd.ppc.spr[regnr];
67                    uint32_t lower = cpu->cd.ppc.spr[regnr + 1];
68                    uint32_t phys = lower & BAT_RPN, ebs = upper & BAT_EPI;
69                    uint32_t mask = ((upper & BAT_BL) << 15) | 0x1ffff;
70    
71                    /*  Not valid in either supervisor or user mode?  */
72                    if (user && !(upper & BAT_Vu))
73                            continue;
74                    if (!user && !(upper & BAT_Vs))
75                            continue;
76    
77                    /*  Virtual address mismatch? Then skip.  */
78                    if ((vaddr & ~mask) != (ebs & ~mask))
79                            continue;
80    
81                    *return_paddr = (vaddr & mask) | (phys & ~mask);
82    
83                    pp = lower & BAT_PP;
84                    switch (pp) {
85                    case BAT_PP_NONE:
86                            return 0;
87                    case BAT_PP_RO_S:
88                    case BAT_PP_RO:
89                            return (flags & FLAG_WRITEFLAG)? 0 : 1;
90                    default:/*  BAT_PP_RW:  */
91                            return 2;
92                    }
93            }
94    
95            return -1;
96    }
97    
98    
99    /*
100     *  get_pte_low():
101     *
102     *  Scan a PTE group for a cmp (compare) value.
103     *
104     *  Returns 1 if the value was found, and *lowp is set to the low PTE word.
105     *  Returns 0 if no match was found.
106     */
107    static int get_pte_low(struct cpu *cpu, uint64_t pteg_select,
108            uint32_t *lowp, uint32_t cmp)
109    {
110            unsigned char *d = memory_paddr_to_hostaddr(cpu->mem, pteg_select, 1)
111                + (pteg_select & ((1 << BITS_PER_MEMBLOCK) - 1));
112            int i;
113    
114            for (i=0; i<8; i++) {
115                    uint32_t *ep = (uint32_t *) (d + (i << 3)), upper;
116                    upper = *ep;
117                    upper = BE32_TO_HOST(upper);
118    
119                    /*  Valid PTE, and correct api and vsid?  */
120                    if (upper == cmp) {
121                            uint32_t lo = ep[1];
122                            lo = BE32_TO_HOST(lo);
123                            *lowp = lo;
124                            return 1;
125                    }
126            }
127    
128            return 0;
129    }
130    
131    
132    /*
133     *  ppc_vtp32():
134     *
135     *  Virtual to physical address translation (32-bit mode).
136     *
137     *  Returns 1 if a translation was found, 0 if none was found. However, finding
138     *  a translation does not mean that it should be returned; there can be
139     *  a permission violation. *resp is set to 0 for no access, 1 for read-only
140     *  access, or 2 for read/write access.
141     */
142    static int ppc_vtp32(struct cpu *cpu, uint32_t vaddr, uint64_t *return_paddr,
143            int *resp, uint64_t msr, int writeflag, int instr)
144    {
145            int srn = (vaddr >> 28) & 15, api = (vaddr >> 22) & PTE_API;
146            int access, key, match;
147            uint32_t vsid = cpu->cd.ppc.sr[srn] & 0x00ffffff;
148            uint64_t sdr1 = cpu->cd.ppc.spr[SPR_SDR1], htaborg;
149            uint32_t hash1, hash2, pteg_select, tmp;
150            uint32_t lower_pte = 0, cmp;
151    
152            htaborg = sdr1 & 0xffff0000UL;
153    
154            /*  Primary hash:  */
155            hash1 = (vsid & 0x7ffff) ^ ((vaddr >> 12) & 0xffff);
156            tmp = (hash1 >> 10) & (sdr1 & 0x1ff);
157            pteg_select = htaborg & 0xfe000000;
158            pteg_select |= ((hash1 & 0x3ff) << 6);
159            pteg_select |= (htaborg & 0x01ff0000) | (tmp << 16);
160            cpu->cd.ppc.spr[SPR_HASH1] = pteg_select;
161            cmp = cpu->cd.ppc.spr[instr? SPR_ICMP : SPR_DCMP] =
162                PTE_VALID | api | (vsid << PTE_VSID_SHFT);
163            match = get_pte_low(cpu, pteg_select, &lower_pte, cmp);
164    
165            /*  Secondary hash:  */
166            hash2 = hash1 ^ 0x7ffff;
167            tmp = (hash2 >> 10) & (sdr1 & 0x1ff);
168            pteg_select = htaborg & 0xfe000000;
169            pteg_select |= ((hash2 & 0x3ff) << 6);
170            pteg_select |= (htaborg & 0x01ff0000) | (tmp << 16);
171            cpu->cd.ppc.spr[SPR_HASH2] = pteg_select;
172            if (!match) {
173                    cmp |= PTE_HID;
174                    match = get_pte_low(cpu, pteg_select, &lower_pte, cmp);
175            }
176    
177            *resp = 0;
178    
179            if (!match)
180                    return 0;
181    
182            /*  Non-executable, or Guarded page?  */
183            if (instr && cpu->cd.ppc.sr[srn] & SR_NOEXEC)
184                    return 1;
185            if (instr && lower_pte & PTE_G)
186                    return 1;
187    
188            access = lower_pte & PTE_PP;
189            *return_paddr = (lower_pte & PTE_RPGN) | (vaddr & ~PTE_RPGN);
190    
191            key = (cpu->cd.ppc.sr[srn] & SR_PRKEY && msr & PPC_MSR_PR) ||
192                (cpu->cd.ppc.sr[srn] & SR_SUKEY && !(msr & PPC_MSR_PR));
193    
194            if (key) {
195                    switch (access) {
196                    case 1:
197                    case 3: *resp = writeflag? 0 : 1;
198                            break;
199                    case 2: *resp = 2;
200                            break;
201                    }
202            } else {
203                    switch (access) {
204                    case 3: *resp = writeflag? 0 : 1;
205                            break;
206                    default:*resp = 2;
207                    }
208            }
209    
210            return 1;
211    }
212    
213    
214  /*  /*
215   *  ppc_translate_address():   *  ppc_translate_v2p():
216   *   *
217   *  Don't call this function is userland_emul is non-NULL, or cpu is NULL.   *  Don't call this function is userland_emul is non-NULL, or cpu is NULL.
218   *   *
# Line 41  Line 221 
221   *      1  Success, the page is readable only   *      1  Success, the page is readable only
222   *      2  Success, the page is read/write   *      2  Success, the page is read/write
223   */   */
224  int ppc_translate_address(struct cpu *cpu, uint64_t vaddr,  int ppc_translate_v2p(struct cpu *cpu, uint64_t vaddr,
225          uint64_t *return_addr, int flags)          uint64_t *return_paddr, int flags)
226  {  {
227          int instr = flags & FLAG_INSTR;          int instr = flags & FLAG_INSTR, res = 0, match, user;
228            int writeflag = flags & FLAG_WRITEFLAG? 1 : 0;
229            uint64_t msr;
230    
231            reg_access_msr(cpu, &msr, 0, 0);
232            user = msr & PPC_MSR_PR? 1 : 0;
233    
234          if (cpu->cd.ppc.bits == 32)          if (cpu->cd.ppc.bits == 32)
235                  vaddr &= 0xffffffff;                  vaddr &= 0xffffffff;
236    
237          if ((instr && !(cpu->cd.ppc.msr & PPC_MSR_IR)) ||          if ((instr && !(msr & PPC_MSR_IR)) || (!instr && !(msr & PPC_MSR_DR))) {
238              (!instr && !(cpu->cd.ppc.msr & PPC_MSR_DR))) {                  *return_paddr = vaddr;
                 *return_addr = vaddr;  
239                  return 2;                  return 2;
240          }          }
241    
242          /*  TODO: This is not really correct.  */          if (cpu->cd.ppc.cpu_type.flags & PPC_601) {
243                    fatal("ppc_translate_v2p(): TODO: 601\n");
244                    exit(1);
245            }
246    
247          if (cpu->cd.ppc.sdr1 == 0) {          /*  Try the BATs first:  */
248                  /*  Try the BATs:  */          if (cpu->cd.ppc.bits == 32) {
249                  int i;                  res = ppc_bat(cpu, vaddr, return_paddr, flags, user);
250                  /*  TODO: This is just a quick (incorrect) hack:  */                  if (res > 0)
251                  for (i=0; i<4; i++) {                          return res;
252                          uint32_t p = 0, v = vaddr & 0xf0000000;                  if (res == 0) {
253                          int match = 0;                          fatal("[ TODO: BAT exception ]\n");
254                          if (instr && (cpu->cd.ppc.ibat_u[i]&0xf0000000) == v) {                          exit(1);
255                                  match = 1;                  }
256                                  p = cpu->cd.ppc.ibat_l[i] & 0xf0000000;          }
257                          }  
258                          /*  Linux/BeBox seems to use data bats for          /*  Virtual to physical translation:  */
259                              instructions?  */          if (cpu->cd.ppc.bits == 32) {
260                          if (!match && (cpu->cd.ppc.dbat_u[i]&0xf0000000) == v) {                  match = ppc_vtp32(cpu, vaddr, return_paddr, &res, msr,
261                                  match = 1;                      writeflag, instr);
262                                  p = cpu->cd.ppc.dbat_l[i] & 0xf0000000;                  if (match && res > 0)
263                          }                          return res;
264                          if (match) {          } else {
265                                  *return_addr = (vaddr & 0x0fffffff) | p;                  /*  htaborg = sdr1 & 0xfffffffffffc0000ULL;  */
266                                  return 2;                  fatal("TODO: ppc 64-bit translation\n");
267                          }                  exit(1);
                 }  
   
                 fatal("ppc_translate_address(): vaddr = 0x%016llx, no ",  
                     "BAT hit?\n", (long long)vaddr);  
                 *return_addr = vaddr;  
                 return 2;  
268          }          }
269    
270          /*  Return failure:  */  
271            /*
272             *  No match? Then cause an exception.
273             *
274             *  PPC603: cause a software TLB reload exception.
275             *  All others: cause a DSI or ISI.
276             */
277    
278          if (flags & FLAG_NOEXCEPTIONS)          if (flags & FLAG_NOEXCEPTIONS)
279                  return 0;                  return 0;
280    
281  #if 1          if (!quiet_mode)
282          /*  TODO: Cause exception.  */                  fatal("[ memory_ppc: exception! vaddr=0x%"PRIx64" pc=0x%"PRIx64
283          fatal("TODO: exception! sdr1 = 0x%llx\n", (long long)cpu->cd.ppc.sdr1);                      " instr=%i user=%i wf=%i ]\n", (uint64_t) vaddr,
284          *return_addr = vaddr;                      (uint64_t) cpu->pc, instr, user, writeflag);
285          return 2;  
286  #else          if (cpu->cd.ppc.cpu_type.flags & PPC_603) {
287                    cpu->cd.ppc.spr[instr? SPR_IMISS : SPR_DMISS] = vaddr;
288    
289                    msr |= PPC_MSR_TGPR;
290                    reg_access_msr(cpu, &msr, 1, 0);
291    
292                    ppc_exception(cpu, instr? 0x10 : (writeflag? 0x12 : 0x11));
293            } else {
294                    if (!instr) {
295                            cpu->cd.ppc.spr[SPR_DAR] = vaddr;
296                            cpu->cd.ppc.spr[SPR_DSISR] = match?
297                                DSISR_PROTECT : DSISR_NOTFOUND;
298                            if (writeflag)
299                                    cpu->cd.ppc.spr[SPR_DSISR] |= DSISR_STORE;
300                    }
301                    ppc_exception(cpu, instr?
302                        PPC_EXCEPTION_ISI : PPC_EXCEPTION_DSI);
303            }
304    
305          return 0;          return 0;
 #endif  
306  }  }
307    

Legend:
Removed from v.18  
changed lines
  Added in v.26

  ViewVC Help
Powered by ViewVC 1.1.26