1 /* 2 * HPPA interrupt helper routines 3 * 4 * Copyright (c) 2017 Richard Henderson 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.1 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 20 #include "qemu/osdep.h" 21 #include "cpu.h" 22 #include "migration/cpu.h" 23 24 25 static int get_psw(QEMUFile *f, void *opaque, size_t size, 26 const VMStateField *field) 27 { 28 CPUHPPAState *env = opaque; 29 cpu_hppa_put_psw(env, qemu_get_be64(f)); 30 return 0; 31 } 32 33 static int put_psw(QEMUFile *f, void *opaque, size_t size, 34 const VMStateField *field, JSONWriter *vmdesc) 35 { 36 CPUHPPAState *env = opaque; 37 qemu_put_be64(f, cpu_hppa_get_psw(env)); 38 return 0; 39 } 40 41 static const VMStateInfo vmstate_psw = { 42 .name = "psw", 43 .get = get_psw, 44 .put = put_psw, 45 }; 46 47 /* FIXME: Use the PA2.0 format, which is a superset of the PA1.1 format. */ 48 static int get_tlb(QEMUFile *f, void *opaque, size_t size, 49 const VMStateField *field) 50 { 51 HPPATLBEntry *ent = opaque; 52 uint32_t val; 53 54 ent->itree.start = qemu_get_be64(f); 55 ent->pa = qemu_get_be64(f); 56 val = qemu_get_be32(f); 57 58 ent->entry_valid = extract32(val, 0, 1); 59 ent->access_id = extract32(val, 1, 18); 60 ent->u = extract32(val, 19, 1); 61 ent->ar_pl2 = extract32(val, 20, 2); 62 ent->ar_pl1 = extract32(val, 22, 2); 63 ent->ar_type = extract32(val, 24, 3); 64 ent->b = extract32(val, 27, 1); 65 ent->d = extract32(val, 28, 1); 66 ent->t = extract32(val, 29, 1); 67 68 ent->itree.last = ent->itree.start + TARGET_PAGE_SIZE - 1; 69 return 0; 70 } 71 72 static int put_tlb(QEMUFile *f, void *opaque, size_t size, 73 const VMStateField *field, JSONWriter *vmdesc) 74 { 75 HPPATLBEntry *ent = opaque; 76 uint32_t val = 0; 77 78 if (ent->entry_valid) { 79 val = 1; 80 val = deposit32(val, 1, 18, ent->access_id); 81 val = deposit32(val, 19, 1, ent->u); 82 val = deposit32(val, 20, 2, ent->ar_pl2); 83 val = deposit32(val, 22, 2, ent->ar_pl1); 84 val = deposit32(val, 24, 3, ent->ar_type); 85 val = deposit32(val, 27, 1, ent->b); 86 val = deposit32(val, 28, 1, ent->d); 87 val = deposit32(val, 29, 1, ent->t); 88 } 89 90 qemu_put_be64(f, ent->itree.start); 91 qemu_put_be64(f, ent->pa); 92 qemu_put_be32(f, val); 93 return 0; 94 } 95 96 static const VMStateInfo vmstate_tlb = { 97 .name = "tlb entry", 98 .get = get_tlb, 99 .put = put_tlb, 100 }; 101 102 static int tlb_pre_load(void *opaque) 103 { 104 CPUHPPAState *env = opaque; 105 106 /* 107 * Zap the entire tlb, on-the-side data structures and all. 108 * Each tlb entry will have data re-filled by put_tlb. 109 */ 110 memset(env->tlb, 0, sizeof(env->tlb)); 111 memset(&env->tlb_root, 0, sizeof(env->tlb_root)); 112 env->tlb_unused = NULL; 113 env->tlb_partial = NULL; 114 115 return 0; 116 } 117 118 static int tlb_post_load(void *opaque, int version_id) 119 { 120 CPUHPPAState *env = opaque; 121 uint32_t btlb_entries = HPPA_BTLB_ENTRIES(env); 122 HPPATLBEntry **unused = &env->tlb_unused; 123 HPPATLBEntry *partial = NULL; 124 125 /* 126 * Re-create the interval tree from the valid entries. 127 * Truely invalid entries should have start == end == 0. 128 * Otherwise it should be the in-flight tlb_partial entry. 129 */ 130 for (uint32_t i = 0; i < ARRAY_SIZE(env->tlb); ++i) { 131 HPPATLBEntry *e = &env->tlb[i]; 132 133 if (e->entry_valid) { 134 interval_tree_insert(&e->itree, &env->tlb_root); 135 } else if (i < btlb_entries) { 136 /* btlb not in unused list */ 137 } else if (partial == NULL && e->itree.start < e->itree.last) { 138 partial = e; 139 } else { 140 *unused = e; 141 unused = &e->unused_next; 142 } 143 } 144 env->tlb_partial = partial; 145 *unused = NULL; 146 147 return 0; 148 } 149 150 static VMStateField vmstate_env_fields[] = { 151 VMSTATE_UINT64_ARRAY(gr, CPUHPPAState, 32), 152 VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32), 153 VMSTATE_UINT64_ARRAY(sr, CPUHPPAState, 8), 154 VMSTATE_UINT64_ARRAY(cr, CPUHPPAState, 32), 155 VMSTATE_UINT64_ARRAY(cr_back, CPUHPPAState, 2), 156 VMSTATE_UINT64_ARRAY(shadow, CPUHPPAState, 7), 157 158 /* Save the architecture value of the psw, not the internally 159 expanded version. Since this architecture value does not 160 exist in memory to be stored, this requires a but of hoop 161 jumping. We want OFFSET=0 so that we effectively pass ENV 162 to the helper functions, and we need to fill in the name by 163 hand since there's no field of that name. */ 164 { 165 .name = "psw", 166 .version_id = 0, 167 .size = sizeof(uint64_t), 168 .info = &vmstate_psw, 169 .flags = VMS_SINGLE, 170 .offset = 0 171 }, 172 173 VMSTATE_UINT64(iaoq_f, CPUHPPAState), 174 VMSTATE_UINT64(iaoq_b, CPUHPPAState), 175 VMSTATE_UINT64(iasq_f, CPUHPPAState), 176 VMSTATE_UINT64(iasq_b, CPUHPPAState), 177 178 VMSTATE_UINT32(fr0_shadow, CPUHPPAState), 179 180 VMSTATE_ARRAY(tlb, CPUHPPAState, ARRAY_SIZE(((CPUHPPAState *)0)->tlb), 181 0, vmstate_tlb, HPPATLBEntry), 182 VMSTATE_UINT32(tlb_last, CPUHPPAState), 183 184 VMSTATE_END_OF_LIST() 185 }; 186 187 static const VMStateDescription vmstate_env = { 188 .name = "env", 189 .version_id = 2, 190 .minimum_version_id = 2, 191 .fields = vmstate_env_fields, 192 .pre_load = tlb_pre_load, 193 .post_load = tlb_post_load, 194 }; 195 196 static VMStateField vmstate_cpu_fields[] = { 197 VMSTATE_CPU(), 198 VMSTATE_STRUCT(env, HPPACPU, 1, vmstate_env, CPUHPPAState), 199 VMSTATE_END_OF_LIST() 200 }; 201 202 const VMStateDescription vmstate_hppa_cpu = { 203 .name = "cpu", 204 .version_id = 1, 205 .minimum_version_id = 1, 206 .fields = vmstate_cpu_fields, 207 }; 208