Statistics
| Branch: | Revision:

root / softmmu_template.h @ b769d8fe

History | View | Annotate | Download (10 kB)

1
/*
2
 *  Software MMU support
3
 * 
4
 *  Copyright (c) 2003 Fabrice Bellard
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 */
20
#define DATA_SIZE (1 << SHIFT)
21

    
22
#if DATA_SIZE == 8
23
#define SUFFIX q
24
#define USUFFIX q
25
#define DATA_TYPE uint64_t
26
#elif DATA_SIZE == 4
27
#define SUFFIX l
28
#define USUFFIX l
29
#define DATA_TYPE uint32_t
30
#elif DATA_SIZE == 2
31
#define SUFFIX w
32
#define USUFFIX uw
33
#define DATA_TYPE uint16_t
34
#elif DATA_SIZE == 1
35
#define SUFFIX b
36
#define USUFFIX ub
37
#define DATA_TYPE uint8_t
38
#else
39
#error unsupported data size
40
#endif
41

    
42
#ifdef SOFTMMU_CODE_ACCESS
43
#define READ_ACCESS_TYPE 2
44
#else
45
#define READ_ACCESS_TYPE 0
46
#endif
47

    
48
static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(unsigned long addr, 
49
                                                        int is_user,
50
                                                        void *retaddr);
51
static inline DATA_TYPE glue(io_read, SUFFIX)(unsigned long physaddr, 
52
                                              unsigned long tlb_addr)
53
{
54
    DATA_TYPE res;
55
    int index;
56

    
57
    index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
58
#if SHIFT <= 2
59
    res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
60
#else
61
#ifdef TARGET_WORDS_BIGENDIAN
62
    res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32;
63
    res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4);
64
#else
65
    res = io_mem_read[index][2](io_mem_opaque[index], physaddr);
66
    res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32;
67
#endif
68
#endif /* SHIFT > 2 */
69
    return res;
70
}
71

    
72
/* handle all cases except unaligned access which span two pages */
73
DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(unsigned long addr,
74
                                                         int is_user)
75
{
76
    DATA_TYPE res;
77
    int index;
78
    unsigned long physaddr, tlb_addr;
79
    void *retaddr;
80
    
81
    /* test if there is match for unaligned or IO access */
82
    /* XXX: could done more in memory macro in a non portable way */
83
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
84
 redo:
85
    tlb_addr = env->tlb_read[is_user][index].address;
86
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
87
        physaddr = addr + env->tlb_read[is_user][index].addend;
88
        if (tlb_addr & ~TARGET_PAGE_MASK) {
89
            /* IO access */
90
            if ((addr & (DATA_SIZE - 1)) != 0)
91
                goto do_unaligned_access;
92
            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
93
        } else if (((addr & 0xfff) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
94
            /* slow unaligned access (it spans two pages or IO) */
95
        do_unaligned_access:
96
            retaddr = GETPC();
97
            res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr, 
98
                                                         is_user, retaddr);
99
        } else {
100
            /* unaligned access in the same page */
101
            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
102
        }
103
    } else {
104
        /* the page is not in the TLB : fill it */
105
        retaddr = GETPC();
106
        tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr);
107
        goto redo;
108
    }
109
    return res;
110
}
111

    
112
/* handle all unaligned cases */
113
static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(unsigned long addr, 
114
                                                        int is_user,
115
                                                        void *retaddr)
116
{
117
    DATA_TYPE res, res1, res2;
118
    int index, shift;
119
    unsigned long physaddr, tlb_addr, addr1, addr2;
120

    
121
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
122
 redo:
123
    tlb_addr = env->tlb_read[is_user][index].address;
124
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
125
        physaddr = addr + env->tlb_read[is_user][index].addend;
126
        if (tlb_addr & ~TARGET_PAGE_MASK) {
127
            /* IO access */
128
            if ((addr & (DATA_SIZE - 1)) != 0)
129
                goto do_unaligned_access;
130
            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
131
        } else if (((addr & 0xfff) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
132
        do_unaligned_access:
133
            /* slow unaligned access (it spans two pages) */
134
            addr1 = addr & ~(DATA_SIZE - 1);
135
            addr2 = addr1 + DATA_SIZE;
136
            res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1, 
137
                                                          is_user, retaddr);
138
            res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2, 
139
                                                          is_user, retaddr);
140
            shift = (addr & (DATA_SIZE - 1)) * 8;
141
#ifdef TARGET_WORDS_BIGENDIAN
142
            res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
143
#else
144
            res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
145
#endif
146
            res = (DATA_TYPE)res;
147
        } else {
148
            /* unaligned/aligned access in the same page */
149
            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
150
        }
151
    } else {
152
        /* the page is not in the TLB : fill it */
153
        tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr);
154
        goto redo;
155
    }
156
    return res;
157
}
158

    
159
#ifndef SOFTMMU_CODE_ACCESS
160

    
161
static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(unsigned long addr, 
162
                                                   DATA_TYPE val, 
163
                                                   int is_user,
164
                                                   void *retaddr);
165

    
166
static inline void glue(io_write, SUFFIX)(unsigned long physaddr, 
167
                                          DATA_TYPE val,
168
                                          unsigned long tlb_addr,
169
                                          void *retaddr)
170
{
171
    int index;
172

    
173
    index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
174
    env->mem_write_vaddr = tlb_addr;
175
    env->mem_write_pc = (unsigned long)retaddr;
176
#if SHIFT <= 2
177
    io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
178
#else
179
#ifdef TARGET_WORDS_BIGENDIAN
180
    io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32);
181
    io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val);
182
#else
183
    io_mem_write[index][2](io_mem_opaque[index], physaddr, val);
184
    io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32);
185
#endif
186
#endif /* SHIFT > 2 */
187
}
188

    
189
void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(unsigned long addr, 
190
                                                    DATA_TYPE val,
191
                                                    int is_user)
192
{
193
    unsigned long physaddr, tlb_addr;
194
    void *retaddr;
195
    int index;
196
    
197
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
198
 redo:
199
    tlb_addr = env->tlb_write[is_user][index].address;
200
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
201
        physaddr = addr + env->tlb_write[is_user][index].addend;
202
        if (tlb_addr & ~TARGET_PAGE_MASK) {
203
            /* IO access */
204
            if ((addr & (DATA_SIZE - 1)) != 0)
205
                goto do_unaligned_access;
206
            retaddr = GETPC();
207
            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
208
        } else if (((addr & 0xfff) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
209
        do_unaligned_access:
210
            retaddr = GETPC();
211
            glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val, 
212
                                                   is_user, retaddr);
213
        } else {
214
            /* aligned/unaligned access in the same page */
215
            glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, val);
216
        }
217
    } else {
218
        /* the page is not in the TLB : fill it */
219
        retaddr = GETPC();
220
        tlb_fill(addr, 1, is_user, retaddr);
221
        goto redo;
222
    }
223
}
224

    
225
/* handles all unaligned cases */
226
static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(unsigned long addr, 
227
                                                   DATA_TYPE val,
228
                                                   int is_user,
229
                                                   void *retaddr)
230
{
231
    unsigned long physaddr, tlb_addr;
232
    int index, i;
233

    
234
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
235
 redo:
236
    tlb_addr = env->tlb_write[is_user][index].address;
237
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
238
        physaddr = addr + env->tlb_write[is_user][index].addend;
239
        if (tlb_addr & ~TARGET_PAGE_MASK) {
240
            /* IO access */
241
            if ((addr & (DATA_SIZE - 1)) != 0)
242
                goto do_unaligned_access;
243
            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
244
        } else if (((addr & 0xfff) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
245
        do_unaligned_access:
246
            /* XXX: not efficient, but simple */
247
            for(i = 0;i < DATA_SIZE; i++) {
248
#ifdef TARGET_WORDS_BIGENDIAN
249
                glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)), 
250
                                          is_user, retaddr);
251
#else
252
                glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8), 
253
                                          is_user, retaddr);
254
#endif
255
            }
256
        } else {
257
            /* aligned/unaligned access in the same page */
258
            glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, val);
259
        }
260
    } else {
261
        /* the page is not in the TLB : fill it */
262
        tlb_fill(addr, 1, is_user, retaddr);
263
        goto redo;
264
    }
265
}
266

    
267
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
268

    
269
#undef READ_ACCESS_TYPE
270
#undef SHIFT
271
#undef DATA_TYPE
272
#undef SUFFIX
273
#undef USUFFIX
274
#undef DATA_SIZE