Statistics
| Branch: | Revision:

root / hw / spapr_iommu.c @ 37952117

History | View | Annotate | Download (6.3 kB)

1
/*
2
 * QEMU sPAPR IOMMU (TCE) code
3
 *
4
 * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
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, see <http://www.gnu.org/licenses/>.
18
 */
19
#include "hw.h"
20
#include "kvm.h"
21
#include "qdev.h"
22
#include "kvm_ppc.h"
23
#include "dma.h"
24

    
25
#include "hw/spapr.h"
26

    
27
#include <libfdt.h>
28

    
29
/* #define DEBUG_TCE */
30

    
31
enum sPAPRTCEAccess {
32
    SPAPR_TCE_FAULT = 0,
33
    SPAPR_TCE_RO = 1,
34
    SPAPR_TCE_WO = 2,
35
    SPAPR_TCE_RW = 3,
36
};
37

    
38
typedef struct sPAPRTCETable sPAPRTCETable;
39

    
40
struct sPAPRTCETable {
41
    DMAContext dma;
42
    uint32_t liobn;
43
    uint32_t window_size;
44
    sPAPRTCE *table;
45
    int fd;
46
    QLIST_ENTRY(sPAPRTCETable) list;
47
};
48

    
49

    
50
QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
51

    
52
static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
53
{
54
    sPAPRTCETable *tcet;
55

    
56
    QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
57
        if (tcet->liobn == liobn) {
58
            return tcet;
59
        }
60
    }
61

    
62
    return NULL;
63
}
64

    
65
static int spapr_tce_translate(DMAContext *dma,
66
                               dma_addr_t addr,
67
                               target_phys_addr_t *paddr,
68
                               target_phys_addr_t *len,
69
                               DMADirection dir)
70
{
71
    sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
72
    enum sPAPRTCEAccess access = (dir == DMA_DIRECTION_FROM_DEVICE)
73
        ? SPAPR_TCE_WO : SPAPR_TCE_RO;
74
    uint64_t tce;
75

    
76
#ifdef DEBUG_TCE
77
    fprintf(stderr, "spapr_tce_translate liobn=0x%" PRIx32 " addr=0x"
78
            DMA_ADDR_FMT "\n", tcet->liobn, addr);
79
#endif
80

    
81
    /* Check if we are in bound */
82
    if (addr >= tcet->window_size) {
83
#ifdef DEBUG_TCE
84
        fprintf(stderr, "spapr_tce_translate out of bounds\n");
85
#endif
86
        return -EFAULT;
87
    }
88

    
89
    tce = tcet->table[addr >> SPAPR_TCE_PAGE_SHIFT].tce;
90

    
91
    /* Check TCE */
92
    if (!(tce & access)) {
93
        return -EPERM;
94
    }
95

    
96
    /* How much til end of page ? */
97
    *len = ((~addr) & SPAPR_TCE_PAGE_MASK) + 1;
98

    
99
    /* Translate */
100
    *paddr = (tce & ~SPAPR_TCE_PAGE_MASK) |
101
        (addr & SPAPR_TCE_PAGE_MASK);
102

    
103
#ifdef DEBUG_TCE
104
    fprintf(stderr, " ->  *paddr=0x" TARGET_FMT_plx ", *len=0x"
105
            TARGET_FMT_plx "\n", *paddr, *len);
106
#endif
107

    
108
    return 0;
109
}
110

    
111
DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size)
112
{
113
    sPAPRTCETable *tcet;
114

    
115
    if (!window_size) {
116
        return NULL;
117
    }
118

    
119
    tcet = g_malloc0(sizeof(*tcet));
120
    dma_context_init(&tcet->dma, spapr_tce_translate, NULL, NULL);
121

    
122
    tcet->liobn = liobn;
123
    tcet->window_size = window_size;
124

    
125
    if (kvm_enabled()) {
126
        tcet->table = kvmppc_create_spapr_tce(liobn,
127
                                              window_size,
128
                                              &tcet->fd);
129
    }
130

    
131
    if (!tcet->table) {
132
        size_t table_size = (window_size >> SPAPR_TCE_PAGE_SHIFT)
133
            * sizeof(sPAPRTCE);
134
        tcet->table = g_malloc0(table_size);
135
    }
136

    
137
#ifdef DEBUG_TCE
138
    fprintf(stderr, "spapr_iommu: New TCE table, liobn=0x%x, context @ %p, "
139
            "table @ %p, fd=%d\n", liobn, &tcet->dma, tcet->table, tcet->fd);
140
#endif
141

    
142
    QLIST_INSERT_HEAD(&spapr_tce_tables, tcet, list);
143

    
144
    return &tcet->dma;
145
}
146

    
147
void spapr_tce_free(DMAContext *dma)
148
{
149

    
150
    if (dma) {
151
        sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
152

    
153
        QLIST_REMOVE(tcet, list);
154

    
155
        if (!kvm_enabled() ||
156
            (kvmppc_remove_spapr_tce(tcet->table, tcet->fd,
157
                                     tcet->window_size) != 0)) {
158
            g_free(tcet->table);
159
        }
160

    
161
        g_free(tcet);
162
    }
163
}
164

    
165
static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
166
                                target_ulong tce)
167
{
168
    sPAPRTCE *tcep;
169

    
170
    if (ioba >= tcet->window_size) {
171
        hcall_dprintf("spapr_vio_put_tce on out-of-boards IOBA 0x"
172
                      TARGET_FMT_lx "\n", ioba);
173
        return H_PARAMETER;
174
    }
175

    
176
    tcep = tcet->table + (ioba >> SPAPR_TCE_PAGE_SHIFT);
177
    tcep->tce = tce;
178

    
179
    return H_SUCCESS;
180
}
181

    
182
static target_ulong h_put_tce(CPUPPCState *env, sPAPREnvironment *spapr,
183
                              target_ulong opcode, target_ulong *args)
184
{
185
    target_ulong liobn = args[0];
186
    target_ulong ioba = args[1];
187
    target_ulong tce = args[2];
188
    sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
189

    
190
    if (liobn & 0xFFFFFFFF00000000ULL) {
191
        hcall_dprintf("spapr_vio_put_tce on out-of-boundsw LIOBN "
192
                      TARGET_FMT_lx "\n", liobn);
193
        return H_PARAMETER;
194
    }
195

    
196
    ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
197

    
198
    if (tcet) {
199
        return put_tce_emu(tcet, ioba, tce);
200
    }
201
#ifdef DEBUG_TCE
202
    fprintf(stderr, "%s on liobn=" TARGET_FMT_lx /*%s*/
203
            "  ioba 0x" TARGET_FMT_lx "  TCE 0x" TARGET_FMT_lx "\n",
204
            __func__, liobn, /*dev->qdev.id, */ioba, tce);
205
#endif
206

    
207
    return H_PARAMETER;
208
}
209

    
210
void spapr_iommu_init(void)
211
{
212
    QLIST_INIT(&spapr_tce_tables);
213

    
214
    /* hcall-tce */
215
    spapr_register_hypercall(H_PUT_TCE, h_put_tce);
216
}
217

    
218
int spapr_dma_dt(void *fdt, int node_off, const char *propname,
219
                 DMAContext *dma)
220
{
221
    if (dma) {
222
        sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
223
        uint32_t dma_prop[] = {cpu_to_be32(tcet->liobn),
224
                               0, 0,
225
                               0, cpu_to_be32(tcet->window_size)};
226
        int ret;
227

    
228
        ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-address-cells", 2);
229
        if (ret < 0) {
230
            return ret;
231
        }
232

    
233
        ret = fdt_setprop_cell(fdt, node_off, "ibm,#dma-size-cells", 2);
234
        if (ret < 0) {
235
            return ret;
236
        }
237

    
238
        ret = fdt_setprop(fdt, node_off, propname, dma_prop,
239
                          sizeof(dma_prop));
240
        if (ret < 0) {
241
            return ret;
242
        }
243
    }
244

    
245
    return 0;
246
}