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

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

  ViewVC Help
Powered by ViewVC 1.1.26