Statistics
| Branch: | Revision:

root / softmmu_template.h @ 97eb5b14

History | View | Annotate | Download (9.5 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
static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(unsigned long addr, 
43
                                                        int is_user,
44
                                                        void *retaddr);
45
static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(unsigned long addr, 
46
                                                   DATA_TYPE val, 
47
                                                   int is_user,
48
                                                   void *retaddr);
49

    
50
static inline DATA_TYPE glue(io_read, SUFFIX)(unsigned long physaddr, 
51
                                              unsigned long tlb_addr)
52
{
53
    DATA_TYPE res;
54
    int index;
55

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

    
71
static inline void glue(io_write, SUFFIX)(unsigned long physaddr, 
72
                                          DATA_TYPE val,
73
                                          unsigned long tlb_addr)
74
{
75
    int index;
76

    
77
    index = (tlb_addr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
78
#if SHIFT <= 2
79
    io_mem_write[index][SHIFT](physaddr, val, tlb_addr);
80
#else
81
#ifdef TARGET_WORDS_BIGENDIAN
82
    io_mem_write[index][2](physaddr, val >> 32, tlb_addr);
83
    io_mem_write[index][2](physaddr + 4, val, tlb_addr);
84
#else
85
    io_mem_write[index][2](physaddr, val, tlb_addr);
86
    io_mem_write[index][2](physaddr + 4, val >> 32, tlb_addr);
87
#endif
88
#endif /* SHIFT > 2 */
89
}
90

    
91
/* handle all cases except unaligned access which span two pages */
92
DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(unsigned long addr,
93
                                                         int is_user)
94
{
95
    DATA_TYPE res;
96
    int index;
97
    unsigned long physaddr, tlb_addr;
98
    void *retaddr;
99
    
100
    /* test if there is match for unaligned or IO access */
101
    /* XXX: could done more in memory macro in a non portable way */
102
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
103
 redo:
104
    tlb_addr = env->tlb_read[is_user][index].address;
105
    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
106
        physaddr = addr + env->tlb_read[is_user][index].addend;
107
        if (tlb_addr & ~TARGET_PAGE_MASK) {
108
            /* IO access */
109
            if ((addr & (DATA_SIZE - 1)) != 0)
110
                goto do_unaligned_access;
111
            res = glue(io_read, SUFFIX)(physaddr, tlb_addr);
112
        } else if (((addr & 0xfff) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
113
            /* slow unaligned access (it spans two pages or IO) */
114
        do_unaligned_access:
115
            retaddr = GETPC();
116
            res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr, 
117
                                                         is_user, retaddr);
118
        } else {
119
            /* unaligned access in the same page */
120
            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
121
        }
122
    } else {
123
        /* the page is not in the TLB : fill it */
124
        retaddr = GETPC();
125
        tlb_fill(addr, 0, is_user, retaddr);
126
        goto redo;
127
    }
128
    return res;
129
}
130

    
131
/* handle all unaligned cases */
132
static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(unsigned long addr, 
133
                                                        int is_user,
134
                                                        void *retaddr)
135
{
136
    DATA_TYPE res, res1, res2;
137
    int index, shift;
138
    unsigned long physaddr, tlb_addr, addr1, addr2;
139

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

    
178

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

    
214
/* handles all unaligned cases */
215
static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(unsigned long addr, 
216
                                                   DATA_TYPE val,
217
                                                   int is_user,
218
                                                   void *retaddr)
219
{
220
    unsigned long physaddr, tlb_addr;
221
    int index, i;
222

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

    
256
#undef SHIFT
257
#undef DATA_TYPE
258
#undef SUFFIX
259
#undef USUFFIX
260
#undef DATA_SIZE