aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/arm/decode.c
blob: abe9f26884cc387bbefb80e2ad2cd4e87df8006a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * xen/arch/arm/decode.c
 *
 * Instruction decoder
 *
 * Julien Grall <julien.grall@linaro.org>
 * Copyright (C) 2013 Linaro Limited.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 */

#include <xen/types.h>
#include <xen/sched.h>
#include <asm/current.h>
#include <asm/guest_access.h>
#include <xen/lib.h>

#include "decode.h"

static void update_dabt(struct hsr_dabt *dabt, int reg,
                        uint8_t size, bool_t sign)
{
    dabt->reg = reg;
    dabt->size = size;
    dabt->sign = sign;
}

static int decode_thumb2(register_t pc, struct hsr_dabt *dabt, uint16_t hw1)
{
    uint16_t hw2;
    int rc;
    uint16_t rt;

    rc = raw_copy_from_guest(&hw2, (void *__user)(pc + 2), sizeof (hw2));
    if ( rc )
        return rc;

    rt = (hw2 >> 12) & 0x7;

    switch ( (hw1 >> 9) & 0xf )
    {
    case 12:
    {
        bool_t sign = !!(hw1 & (1 << 8));
        bool_t load = !!(hw1 & (1 << 4));

        if ( (hw1 & 0x0110) == 0x0100 )
            /* NEON instruction */
            goto bad_thumb2;

        if ( (hw1 & 0x0070) == 0x0070 )
            /* Undefined opcodes */
            goto bad_thumb2;

        /* Store/Load single data item */
        if ( rt == 15 )
            /* XXX: Rt == 15 is only invalid for store instruction */
            goto bad_thumb2;

        if ( !load && sign )
            /* Store instruction doesn't support sign extension */
            goto bad_thumb2;

        update_dabt(dabt, rt, (hw1 >> 5) & 3, sign);

        break;
    }
    default:
        goto bad_thumb2;
    }

    return 0;

bad_thumb2:
    gdprintk(XENLOG_ERR, "unhandled THUMB2 instruction 0x%x%x\n", hw1, hw2);

    return 1;
}

static int decode_thumb(register_t pc, struct hsr_dabt *dabt)
{
    uint16_t instr;
    int rc;

    rc = raw_copy_from_guest(&instr, (void * __user)pc, sizeof (instr));
    if ( rc )
        return rc;

    switch ( instr >> 12 )
    {
    case 5:
    {
        /* Load/Store register */
        uint16_t opB = (instr >> 9) & 0x7;
        int reg = instr & 7;

        switch ( opB & 0x3 )
        {
        case 0: /* Non-signed word */
            update_dabt(dabt, reg, 2, 0);
            break;
        case 1: /* Non-signed halfword */
            update_dabt(dabt, reg, 1, 0);
            break;
        case 2: /* Non-signed byte */
            update_dabt(dabt, reg, 0, 0);
            break;
        case 3: /* Signed byte */
            update_dabt(dabt, reg, 0, 1);
            break;
        }

        break;
    }
    case 6:
        /* Load/Store word immediate offset */
        update_dabt(dabt, instr & 7, 2, 0);
        break;
    case 7:
        /* Load/Store byte immediate offset */
        update_dabt(dabt, instr & 7, 0, 0);
        break;
    case 8:
        /* Load/Store halfword immediate offset */
        update_dabt(dabt, instr & 7, 1, 0);
        break;
    case 9:
        /* Load/Store word sp offset */
        update_dabt(dabt, (instr >> 8) & 7, 2, 0);
        break;
    case 14:
        if ( instr & (1 << 11) )
            return decode_thumb2(pc, dabt, instr);
        goto bad_thumb;
    case 15:
        return decode_thumb2(pc, dabt, instr);
    default:
        goto bad_thumb;
    }

    return 0;

bad_thumb:
    gdprintk(XENLOG_ERR, "unhandled THUMB instruction 0x%x\n", instr);
    return 1;
}

int decode_instruction(const struct cpu_user_regs *regs, struct hsr_dabt *dabt)
{
    if ( is_pv32_domain(current->domain) && regs->cpsr & PSR_THUMB )
        return decode_thumb(regs->pc, dabt);

    /* TODO: Handle ARM instruction */
    gdprintk(XENLOG_ERR, "unhandled ARM instruction\n");

    return 1;
}