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

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

bellard's avatar
bellard committed
42
43
#ifdef SOFTMMU_CODE_ACCESS
#define READ_ACCESS_TYPE 2
bellard's avatar
bellard committed
44
#define ADDR_READ addr_code
bellard's avatar
bellard committed
45
46
#else
#define READ_ACCESS_TYPE 0
bellard's avatar
bellard committed
47
#define ADDR_READ addr_read
bellard's avatar
bellard committed
48
49
#endif

bellard's avatar
bellard committed
50
static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, 
bellard's avatar
bellard committed
51
52
                                                        int is_user,
                                                        void *retaddr);
53
static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr, 
bellard's avatar
bellard committed
54
                                              target_ulong tlb_addr)
55
56
57
58
59
60
{
    DATA_TYPE res;
    int index;

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

/* handle all cases except unaligned access which span two pages */
bellard's avatar
bellard committed
75
DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
bellard's avatar
bellard committed
76
                                                         int is_user)
77
78
{
    DATA_TYPE res;
bellard's avatar
bellard committed
79
    int index;
bellard's avatar
bellard committed
80
    target_ulong tlb_addr;
81
    target_phys_addr_t physaddr;
82
83
84
85
86
87
    void *retaddr;
    
    /* test if there is match for unaligned or IO access */
    /* XXX: could done more in memory macro in a non portable way */
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 redo:
bellard's avatar
bellard committed
88
    tlb_addr = env->tlb_table[is_user][index].ADDR_READ;
89
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
bellard's avatar
bellard committed
90
        physaddr = addr + env->tlb_table[is_user][index].addend;
91
92
93
94
95
        if (tlb_addr & ~TARGET_PAGE_MASK) {
            /* IO access */
            if ((addr & (DATA_SIZE - 1)) != 0)
                goto do_unaligned_access;
            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
bellard's avatar
bellard committed
96
        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
97
98
            /* slow unaligned access (it spans two pages or IO) */
        do_unaligned_access:
bellard's avatar
bellard committed
99
            retaddr = GETPC();
100
101
102
#ifdef ALIGNED_ONLY
            do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr);
#endif
bellard's avatar
bellard committed
103
104
            res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr, 
                                                         is_user, retaddr);
105
        } else {
106
107
108
109
110
111
112
            /* unaligned/aligned access in the same page */
#ifdef ALIGNED_ONLY
            if ((addr & (DATA_SIZE - 1)) != 0) {
                retaddr = GETPC();
                do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr);
            }
#endif
113
            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr);
114
115
116
        }
    } else {
        /* the page is not in the TLB : fill it */
bellard's avatar
bellard committed
117
        retaddr = GETPC();
118
119
120
121
#ifdef ALIGNED_ONLY
        if ((addr & (DATA_SIZE - 1)) != 0)
            do_unaligned_access(addr, READ_ACCESS_TYPE, is_user, retaddr);
#endif
bellard's avatar
bellard committed
122
        tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr);
123
124
125
126
127
128
        goto redo;
    }
    return res;
}

/* handle all unaligned cases */
bellard's avatar
bellard committed
129
static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr, 
bellard's avatar
bellard committed
130
131
                                                        int is_user,
                                                        void *retaddr)
132
133
{
    DATA_TYPE res, res1, res2;
bellard's avatar
bellard committed
134
    int index, shift;
135
    target_phys_addr_t physaddr;
bellard's avatar
bellard committed
136
    target_ulong tlb_addr, addr1, addr2;
137
138
139

    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 redo:
bellard's avatar
bellard committed
140
    tlb_addr = env->tlb_table[is_user][index].ADDR_READ;
141
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
bellard's avatar
bellard committed
142
        physaddr = addr + env->tlb_table[is_user][index].addend;
143
144
145
146
147
        if (tlb_addr & ~TARGET_PAGE_MASK) {
            /* IO access */
            if ((addr & (DATA_SIZE - 1)) != 0)
                goto do_unaligned_access;
            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
bellard's avatar
bellard committed
148
        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
149
150
151
152
        do_unaligned_access:
            /* slow unaligned access (it spans two pages) */
            addr1 = addr & ~(DATA_SIZE - 1);
            addr2 = addr1 + DATA_SIZE;
bellard's avatar
bellard committed
153
154
155
156
            res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1, 
                                                          is_user, retaddr);
            res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2, 
                                                          is_user, retaddr);
157
158
159
160
161
162
            shift = (addr & (DATA_SIZE - 1)) * 8;
#ifdef TARGET_WORDS_BIGENDIAN
            res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
#else
            res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
#endif
bellard's avatar
bellard committed
163
            res = (DATA_TYPE)res;
164
165
        } else {
            /* unaligned/aligned access in the same page */
166
            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)physaddr);
167
168
169
        }
    } else {
        /* the page is not in the TLB : fill it */
bellard's avatar
bellard committed
170
        tlb_fill(addr, READ_ACCESS_TYPE, is_user, retaddr);
171
172
173
174
175
        goto redo;
    }
    return res;
}

bellard's avatar
bellard committed
176
177
#ifndef SOFTMMU_CODE_ACCESS

bellard's avatar
bellard committed
178
static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, 
bellard's avatar
bellard committed
179
180
181
182
                                                   DATA_TYPE val, 
                                                   int is_user,
                                                   void *retaddr);

183
static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr, 
bellard's avatar
bellard committed
184
                                          DATA_TYPE val,
bellard's avatar
bellard committed
185
                                          target_ulong tlb_addr,
bellard's avatar
bellard committed
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
                                          void *retaddr)
{
    int index;

    index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
    env->mem_write_vaddr = tlb_addr;
    env->mem_write_pc = (unsigned long)retaddr;
#if SHIFT <= 2
    io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
#else
#ifdef TARGET_WORDS_BIGENDIAN
    io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32);
    io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val);
#else
    io_mem_write[index][2](io_mem_opaque[index], physaddr, val);
    io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32);
#endif
#endif /* SHIFT > 2 */
}
205

bellard's avatar
bellard committed
206
void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, 
bellard's avatar
bellard committed
207
208
                                                    DATA_TYPE val,
                                                    int is_user)
209
{
210
    target_phys_addr_t physaddr;
bellard's avatar
bellard committed
211
    target_ulong tlb_addr;
212
    void *retaddr;
bellard's avatar
bellard committed
213
    int index;
214
215
216
    
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 redo:
bellard's avatar
bellard committed
217
    tlb_addr = env->tlb_table[is_user][index].addr_write;
218
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
bellard's avatar
bellard committed
219
        physaddr = addr + env->tlb_table[is_user][index].addend;
220
221
222
223
        if (tlb_addr & ~TARGET_PAGE_MASK) {
            /* IO access */
            if ((addr & (DATA_SIZE - 1)) != 0)
                goto do_unaligned_access;
bellard's avatar
bellard committed
224
225
            retaddr = GETPC();
            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
bellard's avatar
bellard committed
226
        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
227
        do_unaligned_access:
bellard's avatar
bellard committed
228
            retaddr = GETPC();
229
230
231
#ifdef ALIGNED_ONLY
            do_unaligned_access(addr, 1, is_user, retaddr);
#endif
bellard's avatar
bellard committed
232
233
            glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val, 
                                                   is_user, retaddr);
234
235
        } else {
            /* aligned/unaligned access in the same page */
236
237
238
239
240
241
#ifdef ALIGNED_ONLY
            if ((addr & (DATA_SIZE - 1)) != 0) {
                retaddr = GETPC();
                do_unaligned_access(addr, 1, is_user, retaddr);
            }
#endif
242
            glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val);
243
244
245
        }
    } else {
        /* the page is not in the TLB : fill it */
bellard's avatar
bellard committed
246
        retaddr = GETPC();
247
248
249
250
#ifdef ALIGNED_ONLY
        if ((addr & (DATA_SIZE - 1)) != 0)
            do_unaligned_access(addr, 1, is_user, retaddr);
#endif
bellard's avatar
bellard committed
251
        tlb_fill(addr, 1, is_user, retaddr);
252
253
254
255
256
        goto redo;
    }
}

/* handles all unaligned cases */
bellard's avatar
bellard committed
257
static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr, 
bellard's avatar
bellard committed
258
259
260
                                                   DATA_TYPE val,
                                                   int is_user,
                                                   void *retaddr)
261
{
262
    target_phys_addr_t physaddr;
bellard's avatar
bellard committed
263
    target_ulong tlb_addr;
bellard's avatar
bellard committed
264
    int index, i;
265
266
267

    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 redo:
bellard's avatar
bellard committed
268
    tlb_addr = env->tlb_table[is_user][index].addr_write;
269
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
bellard's avatar
bellard committed
270
        physaddr = addr + env->tlb_table[is_user][index].addend;
271
272
273
274
        if (tlb_addr & ~TARGET_PAGE_MASK) {
            /* IO access */
            if ((addr & (DATA_SIZE - 1)) != 0)
                goto do_unaligned_access;
bellard's avatar
bellard committed
275
            glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
bellard's avatar
bellard committed
276
        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
277
278
279
280
        do_unaligned_access:
            /* XXX: not efficient, but simple */
            for(i = 0;i < DATA_SIZE; i++) {
#ifdef TARGET_WORDS_BIGENDIAN
bellard's avatar
bellard committed
281
282
                glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)), 
                                          is_user, retaddr);
283
#else
bellard's avatar
bellard committed
284
285
                glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8), 
                                          is_user, retaddr);
286
287
288
289
#endif
            }
        } else {
            /* aligned/unaligned access in the same page */
290
            glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val);
291
292
293
        }
    } else {
        /* the page is not in the TLB : fill it */
bellard's avatar
bellard committed
294
        tlb_fill(addr, 1, is_user, retaddr);
295
296
297
298
        goto redo;
    }
}

bellard's avatar
bellard committed
299
300
301
#endif /* !defined(SOFTMMU_CODE_ACCESS) */

#undef READ_ACCESS_TYPE
302
303
304
#undef SHIFT
#undef DATA_TYPE
#undef SUFFIX
bellard's avatar
bellard committed
305
#undef USUFFIX
306
#undef DATA_SIZE
bellard's avatar
bellard committed
307
#undef ADDR_READ