| 1 | /* |
| 2 | * ========== Copyright Header Begin ========================================== |
| 3 | * |
| 4 | * Hypervisor Software File: segments.h |
| 5 | * |
| 6 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. |
| 7 | * |
| 8 | * - Do no alter or remove copyright notices |
| 9 | * |
| 10 | * - Redistribution and use of this software in source and binary forms, with |
| 11 | * or without modification, are permitted provided that the following |
| 12 | * conditions are met: |
| 13 | * |
| 14 | * - Redistribution of source code must retain the above copyright notice, |
| 15 | * this list of conditions and the following disclaimer. |
| 16 | * |
| 17 | * - Redistribution in binary form must reproduce the above copyright notice, |
| 18 | * this list of conditions and the following disclaimer in the |
| 19 | * documentation and/or other materials provided with the distribution. |
| 20 | * |
| 21 | * Neither the name of Sun Microsystems, Inc. or the names of contributors |
| 22 | * may be used to endorse or promote products derived from this software |
| 23 | * without specific prior written permission. |
| 24 | * |
| 25 | * This software is provided "AS IS," without a warranty of any kind. |
| 26 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, |
| 27 | * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A |
| 28 | * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN |
| 29 | * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR |
| 30 | * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR |
| 31 | * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN |
| 32 | * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR |
| 33 | * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE |
| 34 | * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, |
| 35 | * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF |
| 36 | * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. |
| 37 | * |
| 38 | * You acknowledge that this software is not designed, licensed or |
| 39 | * intended for use in the design, construction, operation or maintenance of |
| 40 | * any nuclear facility. |
| 41 | * |
| 42 | * ========== Copyright Header End ============================================ |
| 43 | */ |
| 44 | /* |
| 45 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. |
| 46 | * Use is subject to license terms. |
| 47 | */ |
| 48 | |
| 49 | #ifndef _SEGMENTS_H |
| 50 | #define _SEGMENTS_H |
| 51 | |
| 52 | #pragma ident "@(#)segments.h 1.2 07/05/15 SMI" |
| 53 | |
| 54 | #ifdef __cplusplus |
| 55 | extern "C" { |
| 56 | #endif |
| 57 | |
| 58 | #ifndef _ASM |
| 59 | |
| 60 | struct ra2pa_segment { |
| 61 | uint64_t base; |
| 62 | uint64_t limit; |
| 63 | uint64_t offset; |
| 64 | uint8_t flags; |
| 65 | uint8_t _pad1; |
| 66 | uint16_t _pad2; |
| 67 | uint32_t _pad3; |
| 68 | }; |
| 69 | |
| 70 | typedef struct ra2pa_segment ra2pa_segment_t; |
| 71 | |
| 72 | #endif /* _ASM */ |
| 73 | |
| 74 | |
| 75 | #define INVALID_SEGMENT_SIZE (-1) |
| 76 | |
| 77 | /* flags for ra2pa_segment.flags */ |
| 78 | #define INVALID_SEGMENT (0) |
| 79 | #define MEM_SEGMENT (1 << 0) |
| 80 | #define IO_SEGMENT (1 << 1) |
| 81 | |
| 82 | /* |
| 83 | * The following macros apply for the ra->pa and pa->ra conversion |
| 84 | * using the segmented memory mechanism. |
| 85 | * This allows a guest's memory to be apportioned in multiple |
| 86 | * dis-contiguous blocks, as well as supporting the various |
| 87 | * IO address spaces that are relevent only if the specific IO |
| 88 | * busses are indeed placed in the guest's real address spaces. |
| 89 | * |
| 90 | * This scheme replaces the old linear base & bounds range check which |
| 91 | * was broken anyway. |
| 92 | * |
| 93 | * We use the upper (64-RA2PA_SHIFT) bits of any RA as the index into a |
| 94 | * simple linear "page table". The index retrieves a guest specific |
| 95 | * entry describing a memory segment to be checked and translated. |
| 96 | * A simple base & bounds is checked using values described within the |
| 97 | * segment .. a simple offset is applied to convert the RA to a PA |
| 98 | * within the segment. |
| 99 | * |
| 100 | * Once a segment is found the base RA is guaranteed to be sane. |
| 101 | * However, in many cases the SIZE value comes from the guest |
| 102 | * OS and if negative causes lots of problems with range testing. |
| 103 | * |
| 104 | * So we wrap the weaker form of this macro with one which tests the |
| 105 | * size for being negative before handing off to the weaker macro. |
| 106 | * Though the form with the size check only takes a register as |
| 107 | * its size parameter. The wrapped form can also take a small |
| 108 | * constant. |
| 109 | * |
| 110 | * The largest defined page size for 4v is 34 bits (13+7*3) thus |
| 111 | * we can place each segment at this boundary without the risk |
| 112 | * of having a need for a larger page size. |
| 113 | * Actually the algorithm works for smaller segment sizes too |
| 114 | * because each segment holds a full base and bounds. |
| 115 | * |
| 116 | * The segmentation arrangement causes problems with supporting |
| 117 | * live migration. We are constrained to be able to support the |
| 118 | * same segmentation map on the target system. This is easier |
| 119 | * if the segmentation separation is as wide as possible - hence |
| 120 | * the choice of 34 bits as the index shift. |
| 121 | * |
| 122 | * We arrange the RA bit range to be big enough to also pick up the |
| 123 | * the IO bus address ranges. Fortunately each bus has a separation |
| 124 | * in the address space of at least 34 bits ... |
| 125 | */ |
| 126 | |
| 127 | #define MAX_RA_BITS 40 |
| 128 | #define RA2PA_SHIFT 34 /* largest possible 4v page size */ |
| 129 | |
| 130 | #define RA2PA_SEGMENT_SHIFT 5 /* size of ra2pa_segment struct */ |
| 131 | #define NUM_RA2PA_SEGMENT_BITS (MAX_RA_BITS-RA2PA_SHIFT) |
| 132 | #define NUM_RA2PA_SEGMENTS (1<<NUM_RA2PA_SEGMENT_BITS) |
| 133 | |
| 134 | /* BEGIN CSTYLED */ |
| 135 | #define RA2PA_RANGE_CONV_UNK_SIZE(guestp, raddr, size, fail_label, segp, paddr)\ |
| 136 | brlez,pn size, fail_label ;\ |
| 137 | RA2PA_RANGE_CONV(guestp, raddr, size, fail_label, segp, paddr) |
| 138 | /* END CSTYLED */ |
| 139 | |
| 140 | /* |
| 141 | * Macro to check range assuming that size is limited and |
| 142 | * positive. The first instn of this macro must be able to |
| 143 | * function in the delay slot of the last instn of the outer |
| 144 | * macro. |
| 145 | */ |
| 146 | /* BEGIN CSTYLED */ |
| 147 | #define RA2PA_RANGE_CONV(guestp, raddr, size, fail_label, segp, paddr) \ |
| 148 | srlx raddr, RA2PA_SHIFT, segp ;\ |
| 149 | cmp segp, NUM_RA2PA_SEGMENTS ;\ |
| 150 | /* can branch on signed as srlx makes the number +ve */ ;\ |
| 151 | bge,pn %xcc, fail_label ;\ |
| 152 | sllx segp, RA2PA_SEGMENT_SHIFT, segp ;\ |
| 153 | add segp, GUEST_RA2PA_SEGMENT, segp ;\ |
| 154 | add guestp, segp, segp ;\ |
| 155 | ldub [segp + RA2PA_SEGMENT_FLAGS], paddr ;\ |
| 156 | btst MEM_SEGMENT, paddr ;\ |
| 157 | bz,pn %xcc, fail_label ;\ |
| 158 | ldx [segp + RA2PA_SEGMENT_BASE], paddr ;\ |
| 159 | cmp paddr, raddr ;\ |
| 160 | bgu,pn %xcc, fail_label ;\ |
| 161 | ldx [segp + RA2PA_SEGMENT_LIMIT], paddr ;\ |
| 162 | sub paddr, raddr, paddr ;\ |
| 163 | cmp paddr, size ;\ |
| 164 | blt,pn %xcc, fail_label ;\ |
| 165 | ldx [segp + RA2PA_SEGMENT_OFFSET], paddr ;\ |
| 166 | add paddr, raddr, paddr |
| 167 | /* END CSTYLED */ |
| 168 | |
| 169 | /* |
| 170 | * Check range, no RA->PA conversion |
| 171 | */ |
| 172 | /* BEGIN CSTYLED */ |
| 173 | #define RA2PA_RANGE_CHECK(guestp, raddr, size, fail_label, scr1) \ |
| 174 | srlx raddr, RA2PA_SHIFT, scr1 ;\ |
| 175 | cmp scr1, NUM_RA2PA_SEGMENTS ;\ |
| 176 | /* can branch on signed as srlx makes the number +ve */ ;\ |
| 177 | bge,pn %xcc, fail_label ;\ |
| 178 | sllx scr1, RA2PA_SEGMENT_SHIFT, scr1 ;\ |
| 179 | add scr1, GUEST_RA2PA_SEGMENT, scr1 ;\ |
| 180 | add guestp, scr1, scr1 ;\ |
| 181 | ldub [scr1 + RA2PA_SEGMENT_FLAGS], scr1 ;\ |
| 182 | btst MEM_SEGMENT, scr1 ;\ |
| 183 | bz,pn %xcc, fail_label ;\ |
| 184 | srlx raddr, RA2PA_SHIFT, scr1 ;\ |
| 185 | sllx scr1, RA2PA_SEGMENT_SHIFT, scr1 ;\ |
| 186 | add scr1, GUEST_RA2PA_SEGMENT, scr1 ;\ |
| 187 | add guestp, scr1, scr1 ;\ |
| 188 | ldx [scr1 + RA2PA_SEGMENT_BASE], scr1 ;\ |
| 189 | cmp scr1, raddr ;\ |
| 190 | bgu,pn %xcc, fail_label ;\ |
| 191 | srlx raddr, RA2PA_SHIFT, scr1 ;\ |
| 192 | sllx scr1, RA2PA_SEGMENT_SHIFT, scr1 ;\ |
| 193 | add scr1, GUEST_RA2PA_SEGMENT, scr1 ;\ |
| 194 | add guestp, scr1, scr1 ;\ |
| 195 | ldx [scr1 + RA2PA_SEGMENT_LIMIT], scr1 ;\ |
| 196 | sub scr1, raddr, scr1 ;\ |
| 197 | cmp scr1, size ;\ |
| 198 | blt,pn %xcc, fail_label ;\ |
| 199 | nop |
| 200 | /* END CSTYLED */ |
| 201 | |
| 202 | /* |
| 203 | * Macro to convert RA to PA. RA must be valid for this guest. |
| 204 | */ |
| 205 | /* BEGIN CSTYLED */ |
| 206 | #define RA2PA_CONV(guestp, raddr, paddr, segp) \ |
| 207 | srlx raddr, RA2PA_SHIFT, segp ;\ |
| 208 | sllx segp, RA2PA_SEGMENT_SHIFT, segp ;\ |
| 209 | add segp, GUEST_RA2PA_SEGMENT, segp ;\ |
| 210 | add guestp, segp, segp ;\ |
| 211 | ldx [segp + RA2PA_SEGMENT_OFFSET], segp ;\ |
| 212 | add raddr, segp, paddr |
| 213 | |
| 214 | /* |
| 215 | * macro to get segment pointer for an RA. Returns (0) in segp |
| 216 | * if RA not valid for this guest. |
| 217 | */ |
| 218 | #define RA_GET_SEGMENT(guestp, raddr, segp, scr) \ |
| 219 | srlx raddr, RA2PA_SHIFT, segp ;\ |
| 220 | sllx segp, RA2PA_SEGMENT_SHIFT, segp ;\ |
| 221 | add segp, GUEST_RA2PA_SEGMENT, segp ;\ |
| 222 | add guestp, segp, segp ;\ |
| 223 | ldub [segp + RA2PA_SEGMENT_FLAGS], scr ;\ |
| 224 | btst (MEM_SEGMENT | IO_SEGMENT), scr ;\ |
| 225 | move %xcc, %g0, segp |
| 226 | |
| 227 | /* END CSTYLED */ |
| 228 | |
| 229 | |
| 230 | /* BEGIN CSTYLED */ |
| 231 | /* |
| 232 | * Find the segment containing a PA and return the corresponding RA. |
| 233 | * |
| 234 | * guestp &guest struct, preserved |
| 235 | * paddr Physical Address to translate, preserved |
| 236 | * raddr Real Address for this PA (-1 if no translation) |
| 237 | * scrN clobbered |
| 238 | * scr2 ret of non zero if no segment found containing this PA |
| 239 | */ |
| 240 | #define PA2RA_CONV(guestp, paddr, raddr, scr1, scr2) \ |
| 241 | .pushlocals ;\ |
| 242 | add guestp, ((NUM_RA2PA_SEGMENTS - 1) * RA2PA_SEGMENT_SIZE) \ |
| 243 | + GUEST_RA2PA_SEGMENT, scr1 ;\ |
| 244 | /* &guest.ra2pa_segment[N-1] */ ;\ |
| 245 | 0: ;\ |
| 246 | ldx [scr1 + RA2PA_SEGMENT_BASE], scr2 /* RA base */ ;\ |
| 247 | brlz,pn scr2, 1f /* segment not in use */ ;\ |
| 248 | nop ;\ |
| 249 | ldx [scr1 + RA2PA_SEGMENT_OFFSET], raddr /* RA offset */ ;\ |
| 250 | sub paddr, raddr, raddr /* PA->RA */ ;\ |
| 251 | cmp raddr, scr2 /* RA < segment.base ? */ ;\ |
| 252 | blu,pn %xcc, 1f ;\ |
| 253 | nop ;\ |
| 254 | ldx [scr1 + RA2PA_SEGMENT_LIMIT], scr2 /* RA limit */ ;\ |
| 255 | cmp raddr, scr2 /* RA > segment.limit ? */ ;\ |
| 256 | bgu,pn %xcc, 1f /* yes, next segment */ ;\ |
| 257 | nop ;\ |
| 258 | ba 2f /* no, we are done */ ;\ |
| 259 | mov 0, scr2 ;\ |
| 260 | 1: ;\ |
| 261 | add guestp, GUEST_RA2PA_SEGMENT, scr2 ;\ |
| 262 | cmp scr1, scr2 /* scr2 &guest.ra2pa_segment[0] */ ;\ |
| 263 | bne,pt %xcc, 0b /* next segment */ ;\ |
| 264 | sub scr1, RA2PA_SEGMENT_SIZE, scr1 ;\ |
| 265 | mov 1, scr2 /* no segment found, ret fail */ ;\ |
| 266 | mov -1, raddr ;\ |
| 267 | 2: ;\ |
| 268 | .poplocals |
| 269 | /* END CSTYLED */ |
| 270 | |
| 271 | #ifdef CONFIG_PCIE |
| 272 | |
| 273 | /* |
| 274 | * Note: Could just use RA2PA_RANGE_CONV_UNK_SIZE() macro, but that would |
| 275 | * involve an extra memory access for RA2PA_SEGMENT_OFFSET. |
| 276 | */ |
| 277 | /* BEGIN CSTYLED */ |
| 278 | #define RANGE_CHECK_IO(guestp, raddr, size, pass_label, fail_label, segp, scr) \ |
| 279 | srlx raddr, RA2PA_SHIFT, segp ;\ |
| 280 | cmp segp, NUM_RA2PA_SEGMENTS ;\ |
| 281 | /* can branch on signed as srlx makes the number +ve */ ;\ |
| 282 | bge,pn %xcc, fail_label ;\ |
| 283 | sllx segp, RA2PA_SEGMENT_SHIFT, segp ;\ |
| 284 | add segp, GUEST_RA2PA_SEGMENT, segp ;\ |
| 285 | add guestp, segp, segp ;\ |
| 286 | ldub [segp + RA2PA_SEGMENT_FLAGS], scr ;\ |
| 287 | btst IO_SEGMENT, scr ;\ |
| 288 | bz,pn %xcc, fail_label ;\ |
| 289 | ldx [segp + RA2PA_SEGMENT_BASE], scr ;\ |
| 290 | cmp scr, raddr ;\ |
| 291 | bgu,pn %xcc, fail_label ;\ |
| 292 | ldx [segp + RA2PA_SEGMENT_LIMIT], scr ;\ |
| 293 | sub scr, raddr, scr ;\ |
| 294 | cmp scr, size ;\ |
| 295 | bge,pn %xcc, pass_label ;\ |
| 296 | nop ;\ |
| 297 | ba %xcc, fail_label ;\ |
| 298 | nop |
| 299 | #else /* !CONFIG_PCIE */ |
| 300 | #define RANGE_CHECK_IO(hstruct, raddr, size, pass_lbl, fail_lbl, \ |
| 301 | scr1, scr2) \ |
| 302 | ba,a fail_lbl ;\ |
| 303 | .empty |
| 304 | /* END CSTYLED */ |
| 305 | #endif /* CONFIG_PCIE */ |
| 306 | |
| 307 | #ifdef __cplusplus |
| 308 | } |
| 309 | #endif |
| 310 | |
| 311 | #endif /* _SEGMENTS_H */ |