| 1 | /* |
| 2 | * ========== Copyright Header Begin ========================================== |
| 3 | * |
| 4 | * OpenSPARC T2 Processor File: sparse_mem.h |
| 5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. |
| 6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. |
| 7 | * |
| 8 | * The above named program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public |
| 10 | * License version 2 as published by the Free Software Foundation. |
| 11 | * |
| 12 | * The above named program is distributed in the hope that it will be |
| 13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public |
| 18 | * License along with this work; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
| 20 | * |
| 21 | * ========== Copyright Header End ============================================ |
| 22 | */ |
| 23 | #ifndef __IOMEM_SPARSE_MEM_H__ |
| 24 | #define __IOMEM_SPARSE_MEM_H__ |
| 25 | |
| 26 | |
| 27 | #include <synch.h> |
| 28 | #include <sys/types.h> |
| 29 | #include <string.h> |
| 30 | #include <stdlib.h> |
| 31 | #include <sys/mman.h> |
| 32 | #include <sys/stat.h> |
| 33 | #include <libelf.h> |
| 34 | #include <assert.h> |
| 35 | #include <ctype.h> |
| 36 | #include <fcntl.h> |
| 37 | #include <unistd.h> |
| 38 | #include "mem_intf.h" |
| 39 | #include "utils.h" |
| 40 | |
| 41 | #if defined(ARCH_X64) |
| 42 | #define switch_endian16(a) (ss_byteswap16(a)) |
| 43 | #define switch_endian32(a) (ss_byteswap32(a)) |
| 44 | #define switch_endian64(a) (ss_byteswap64(a)) |
| 45 | #else |
| 46 | #define switch_endian16(a) (a) |
| 47 | #define switch_endian32(a) (a) |
| 48 | #define switch_endian64(a) (a) |
| 49 | #endif |
| 50 | |
| 51 | // sparse memory model |
| 52 | |
| 53 | class SparseMemory:public ioMemIntf/*{{{*/ |
| 54 | { |
| 55 | char * name; |
| 56 | void SparseMemory::load_elf64_section(Elf64_Shdr *shdr, Elf_Scn *scn, |
| 57 | uint64_t base_pa, uint64_t base_va); |
| 58 | static const int SAM_NMEM_LOCKS = 1<<3; |
| 59 | // 8, should be power of 2 |
| 60 | // maintain coherence at 8 byte level only |
| 61 | public: |
| 62 | SparseMemory( uint64_t ram_size=0, uint_t _l1bits=18, uint_t _l2bits=10, uint_t _l3bits=20 ); |
| 63 | ~SparseMemory(); |
| 64 | |
| 65 | void setId(const char * n){ name = strdup(n); } |
| 66 | uint64_t get_size() { return size; } |
| 67 | |
| 68 | |
| 69 | // get_base() is a private method and should not be used |
| 70 | // outside this module |
| 71 | uint8_t* get_base() { return NULL; } |
| 72 | |
| 73 | |
| 74 | int load_img( const char* mem_image_filename, uint64_t start_pa ); |
| 75 | int load_bin( const char *file, uint64_t addr); |
| 76 | int load_elf( const char *filename, uint64_t base_pa ); |
| 77 | |
| 78 | int save( const char* filename, uint64_t addr, uint64_t size ); |
| 79 | |
| 80 | // Supported Store Operations. st8(), st16(), st32() and st64() are gueranteed to be atomic. |
| 81 | // st128() and st512() are atomic per 64bit quantity. |
| 82 | void st8( uint64_t addr, uint8_t data ) |
| 83 | { |
| 84 | *(uint8_t*)(get_st_ptr(addr)) = data; |
| 85 | } |
| 86 | |
| 87 | void st16( uint64_t addr, uint16_t data ) |
| 88 | { |
| 89 | *(uint16_t*)(get_st_ptr(addr)) = switch_endian16(data); |
| 90 | } |
| 91 | void st32( uint64_t addr, uint32_t data ) |
| 92 | { |
| 93 | *(uint32_t*)(get_st_ptr(addr)) = switch_endian32(data); |
| 94 | } |
| 95 | void st64_nl( uint64_t addr, uint64_t data ) |
| 96 | { |
| 97 | *(uint64_t*)(get_st_ptr(addr)) = switch_endian64(data); |
| 98 | } |
| 99 | void st64 ( uint64_t addr, uint64_t data ) |
| 100 | { |
| 101 | lock(addr); |
| 102 | st64_nl(addr,switch_endian64(data)); |
| 103 | unlock(addr); |
| 104 | } |
| 105 | |
| 106 | void st128( uint64_t addr, uint64_t data[2] ) |
| 107 | { |
| 108 | uint8_t* ptr = get_st_ptr(addr & ~uint64_t(0x1f)); |
| 109 | *(uint64_t*)(ptr + 0) = switch_endian64(data[0]); |
| 110 | *(uint64_t*)(ptr + 8) = switch_endian64(data[1]); |
| 111 | } |
| 112 | void st512( uint64_t addr, uint64_t data[8] ) |
| 113 | { |
| 114 | uint8_t* ptr = get_st_ptr(addr & ~uint64_t(0x3f)); |
| 115 | *(uint64_t*)(ptr + 0) = switch_endian64(data[0]); |
| 116 | *(uint64_t*)(ptr + 8) = switch_endian64(data[1]); |
| 117 | *(uint64_t*)(ptr + 16) = switch_endian64(data[2]); |
| 118 | *(uint64_t*)(ptr + 24) = switch_endian64(data[3]); |
| 119 | *(uint64_t*)(ptr + 32) = switch_endian64(data[4]); |
| 120 | *(uint64_t*)(ptr + 40) = switch_endian64(data[5]); |
| 121 | *(uint64_t*)(ptr + 48) = switch_endian64(data[6]); |
| 122 | *(uint64_t*)(ptr + 56) = switch_endian64(data[7]); |
| 123 | } |
| 124 | |
| 125 | uint8_t ld8u ( uint64_t addr ) |
| 126 | { |
| 127 | return *(uint8_t *)(get_ld_ptr(addr)); |
| 128 | } |
| 129 | |
| 130 | int8_t ld8s( uint64_t addr ) |
| 131 | { |
| 132 | return *( int8_t *)(get_ld_ptr(addr)); |
| 133 | } |
| 134 | uint16_t ld16u( uint64_t addr ) |
| 135 | { |
| 136 | return switch_endian16(*(uint16_t*)(get_ld_ptr(addr))); |
| 137 | } |
| 138 | int16_t ld16s( uint64_t addr ) |
| 139 | { |
| 140 | return switch_endian16(*( int16_t*)(get_ld_ptr(addr))); |
| 141 | } |
| 142 | uint32_t ld32u( uint64_t addr ) |
| 143 | { |
| 144 | return switch_endian32(*(uint32_t*)(get_ld_ptr(addr))); |
| 145 | } |
| 146 | int32_t ld32s( uint64_t addr ) |
| 147 | { |
| 148 | return switch_endian32(*( int32_t*)(get_ld_ptr(addr))); |
| 149 | } |
| 150 | uint64_t ld64( uint64_t addr ) |
| 151 | { |
| 152 | return switch_endian64(*(uint64_t*)(get_ld_ptr(addr))); |
| 153 | } |
| 154 | void ld128( uint64_t addr, uint64_t data[2] ) |
| 155 | { |
| 156 | uint8_t* ptr = get_ld_ptr(addr); |
| 157 | data[0] = switch_endian64(*(uint64_t*)(ptr + 0)); |
| 158 | data[1] = switch_endian64(*(uint64_t*)(ptr + 8)); |
| 159 | } |
| 160 | void ld512( uint64_t addr, uint64_t data[8] ) |
| 161 | { |
| 162 | uint8_t* ptr = get_ld_ptr(addr & ~uint64_t(0x3f)); |
| 163 | data[0] = switch_endian64(*(uint64_t*)(ptr + 0)); |
| 164 | data[1] = switch_endian64(*(uint64_t*)(ptr + 8)); |
| 165 | data[2] = switch_endian64(*(uint64_t*)(ptr + 16)); |
| 166 | data[3] = switch_endian64(*(uint64_t*)(ptr + 24)); |
| 167 | data[4] = switch_endian64(*(uint64_t*)(ptr + 32)); |
| 168 | data[5] = switch_endian64(*(uint64_t*)(ptr + 40)); |
| 169 | data[6] = switch_endian64(*(uint64_t*)(ptr + 48)); |
| 170 | data[7] = switch_endian64(*(uint64_t*)(ptr + 56)); |
| 171 | } |
| 172 | void ld256( uint64_t addr, uint64_t data[4] ) |
| 173 | { |
| 174 | uint8_t* ptr = get_ld_ptr(addr & ~uint64_t(0x1f)); |
| 175 | data[0] = switch_endian64(*(uint64_t*)(ptr + 0)); |
| 176 | data[1] = switch_endian64(*(uint64_t*)(ptr + 8)); |
| 177 | data[2] = switch_endian64(*(uint64_t*)(ptr + 16)); |
| 178 | data[3] = switch_endian64(*(uint64_t*)(ptr + 24)); |
| 179 | } |
| 180 | |
| 181 | |
| 182 | int block_read(uint64_t addr, uint8_t *tgt, int _size); |
| 183 | int block_write(uint64_t addr, const uint8_t *src, int _size); |
| 184 | |
| 185 | int dump ( FILE * fp ); |
| 186 | int restore ( FILE * fp ); |
| 187 | |
| 188 | |
| 189 | // no mem page allocation on load accesses; |
| 190 | // if load goes to uninit location - return unknow value; |
| 191 | uint8_t* get_ld_ptr ( uint64_t addr ) |
| 192 | { |
| 193 | uint8_t*** o1 = (uint8_t***)((char*)l1 + ((addr >> l1shft) & l1mask)); |
| 194 | uint8_t** l2 = *o1; |
| 195 | |
| 196 | if (l2 == 0) |
| 197 | return uninit_page + ( addr & 0x7 ); |
| 198 | |
| 199 | uint8_t** o2 = (uint8_t**)((char*)l2 + ((addr >> l2shft) & l2mask)); |
| 200 | uint8_t* l3 = *o2; |
| 201 | if (l3 == 0) |
| 202 | return uninit_page + ( addr & 0x7 ); |
| 203 | |
| 204 | return l3 + (addr & l3mask); |
| 205 | } |
| 206 | |
| 207 | // allocate mem page if store goes to uninit location; |
| 208 | // acquire a lock to prevent multiple writers on MP run; |
| 209 | uint8_t* get_st_ptr( uint64_t addr ) |
| 210 | { |
| 211 | |
| 212 | uint8_t*** o1 = (uint8_t***)((char*)l1 + ((addr >> l1shft) & l1mask) ); |
| 213 | uint8_t** l2 = *o1; |
| 214 | |
| 215 | if (l2 == 0) |
| 216 | { |
| 217 | mutex_lock(&l2_lock); |
| 218 | |
| 219 | // check again if level 2 table is already allocated |
| 220 | l2 = *o1 ; |
| 221 | if(l2 == 0) |
| 222 | l2 = *o1 = (uint8_t**)calloc(l2size,sizeof(uint8_t)); |
| 223 | |
| 224 | mutex_unlock(&l2_lock); |
| 225 | } |
| 226 | |
| 227 | uint8_t** o2 = (uint8_t**)((char*)l2 + ((addr >> l2shft) & l2mask)); |
| 228 | uint8_t* l3 = *o2; |
| 229 | |
| 230 | if (l3 == 0) |
| 231 | { |
| 232 | mutex_lock(&l3_lock); |
| 233 | |
| 234 | // check again if level 3 page is already allocated |
| 235 | l3 = *o2; |
| 236 | if(l3 == 0) |
| 237 | l3 = *o2 = (uint8_t*)calloc(l3size,sizeof(uint8_t)); |
| 238 | |
| 239 | mutex_unlock ( &l3_lock ); |
| 240 | } |
| 241 | |
| 242 | return l3 + (addr & l3mask); |
| 243 | } |
| 244 | |
| 245 | |
| 246 | uint64_t get_l1size() { return l1size; } |
| 247 | uint64_t get_l2size() { return l2size; } |
| 248 | uint64_t get_l3size() { return l3size; } |
| 249 | |
| 250 | |
| 251 | private: |
| 252 | |
| 253 | void lock ( uint64_t addr ) { mutex_lock ( &locks[(addr >> 4) & (SAM_NMEM_LOCKS -1)] ); } |
| 254 | void unlock ( uint64_t addr ) { mutex_unlock ( &locks[(addr >> 4) & (SAM_NMEM_LOCKS -1)] ); } |
| 255 | |
| 256 | private: |
| 257 | |
| 258 | uint8_t*** l1; |
| 259 | |
| 260 | uint_t l1bits; |
| 261 | uint_t l2bits; |
| 262 | uint_t l3bits; |
| 263 | uint_t l1shft; |
| 264 | uint_t l2shft; |
| 265 | uint64_t l1size; |
| 266 | uint64_t l2size; |
| 267 | uint64_t l3size; |
| 268 | uint64_t l1mask; |
| 269 | uint64_t l2mask; |
| 270 | uint64_t l3mask; |
| 271 | |
| 272 | uint64_t size; |
| 273 | |
| 274 | uint8_t uninit_page[512]; |
| 275 | |
| 276 | mutex_t locks [SAM_NMEM_LOCKS ]; |
| 277 | mutex_t l2_lock; |
| 278 | mutex_t l3_lock; |
| 279 | |
| 280 | }; |
| 281 | |
| 282 | |
| 283 | #endif //__IOMEM_SPARSE_MEM_H__ |