LCOV - code coverage report
Current view: top level - flamenco/vm - fd_vm_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 179 213 84.0 %
Date: 2025-01-08 12:08:44 Functions: 38 650 5.8 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_vm_fd_vm_private_h
       2             : #define HEADER_fd_src_flamenco_vm_fd_vm_private_h
       3             : 
       4             : #include "fd_vm.h"
       5             : 
       6             : #include "../../ballet/sbpf/fd_sbpf_instr.h"
       7             : #include "../../ballet/sbpf/fd_sbpf_opcodes.h"
       8             : #include "../../ballet/murmur3/fd_murmur3.h"
       9             : #include "../runtime/context/fd_exec_txn_ctx.h"
      10             : #include "../runtime/fd_runtime.h"
      11             : #include "../features/fd_features.h"
      12             : 
      13             : /* FD_VM_ALIGN_RUST_{} define the alignments for relevant rust types.
      14             :    Alignments are derived with std::mem::align_of::<T>() and are enforced
      15             :    by the VM (with the exception of v1 loader).
      16             : 
      17             :    In our implementation, when calling FD_VM_MEM_HADDR_ST / FD_VM_MEM_HADDR_LD,
      18             :    we need to make sure we're passing the correct alignment based on the Rust
      19             :    type in the corresponding mapping in Agave.
      20             : 
      21             :    FD_VM_ALIGN_RUST_{} has been generated with this Rust code:
      22             :    ```rust
      23             :       pub type Epoch = u64;
      24             :       pub struct Pubkey(pub [u8; 32]);
      25             :       pub struct AccountMeta {
      26             :           pub lamports: u64,
      27             :           pub rent_epoch: Epoch,
      28             :           pub owner: Pubkey,
      29             :           pub executable: bool,
      30             :       }
      31             : 
      32             :       pub struct PodScalar(pub [u8; 32]);
      33             : 
      34             :       fn main() {
      35             :           println!("u8: {}", std::mem::align_of::<u8>());
      36             :           println!("u32: {}", std::mem::align_of::<u32>());
      37             :           println!("u64: {}", std::mem::align_of::<u64>());
      38             :           println!("u128: {}", std::mem::align_of::<u128>());
      39             :           println!("&[u8]: {}", std::mem::align_of::<&[u8]>());
      40             :           println!("AccountMeta: {}", std::mem::align_of::<AccountMeta>());
      41             :           println!("PodScalar: {}", std::mem::align_of::<PodScalar>());
      42             :           println!("Pubkey: {}", std::mem::align_of::<Pubkey>());
      43             :       }
      44             :     ``` */
      45             : 
      46         291 : #define FD_VM_ALIGN_RUST_U8                       (1UL)
      47             : #define FD_VM_ALIGN_RUST_U32                      (4UL)
      48             : #define FD_VM_ALIGN_RUST_I32                      (4UL)
      49             : #define FD_VM_ALIGN_RUST_U64                      (8UL)
      50             : #define FD_VM_ALIGN_RUST_U128                     (16UL)
      51             : #define FD_VM_ALIGN_RUST_SLICE_U8_REF             (8UL)
      52             : #define FD_VM_ALIGN_RUST_POD_U8_ARRAY             (1UL)
      53             : #define FD_VM_ALIGN_RUST_PUBKEY                   (1UL)
      54             : #define FD_VM_ALIGN_RUST_SYSVAR_CLOCK             (8UL)
      55             : #define FD_VM_ALIGN_RUST_SYSVAR_EPOCH_SCHEDULE    (8UL)
      56             : #define FD_VM_ALIGN_RUST_SYSVAR_FEES              (8UL)
      57             : #define FD_VM_ALIGN_RUST_SYSVAR_RENT              (8UL)
      58             : #define FD_VM_ALIGN_RUST_SYSVAR_LAST_RESTART_SLOT (8UL)
      59             : #define FD_VM_ALIGN_RUST_STABLE_INSTRUCTION       (8UL)
      60             : 
      61             : /* fd_vm_vec_t is the in-memory representation of a vector descriptor.
      62             :    Equal in layout to the Rust slice header &[_] and various vector
      63             :    types in the C version of the syscall API. */
      64             : /* FIXME: WHEN IS VADDR NULL AND/OR SZ 0 OKAY? */
      65             : /* FIXME: MOVE FD_VM_RUST_VEC_T FROM SYSCALL/FD_VM_CPI.H HERE TOO? */
      66             : 
      67             : #define FD_VM_VEC_ALIGN FD_VM_ALIGN_RUST_SLICE_U8_REF
      68             : #define FD_VM_VEC_SIZE  (16UL)
      69             : 
      70             : struct __attribute__((packed)) fd_vm_vec {
      71             :   ulong addr; /* FIXME: NAME -> VADDR */
      72             :   ulong len;  /* FIXME: NAME -> SZ */
      73             : };
      74             : 
      75             : typedef struct fd_vm_vec fd_vm_vec_t;
      76             : 
      77             : /* SBPF version and features
      78             :    https://github.com/solana-labs/rbpf/blob/4b2c3dfb02827a0119cd1587eea9e27499712646/src/program.rs#L22
      79             : 
      80             :    Note: SIMDs enable or disable features, e.g. BPF instructions.
      81             :    If we have macros with names ENABLE vs DISABLE, we have the advantage that
      82             :    the condition is always pretty clear: sbpf_version <= activation_version,
      83             :    but the disadvantage of inconsistent names.
      84             :    Viceversa, calling everything ENABLE has the risk to invert a <= with a >=
      85             :    and create a huge mess.
      86             :    We define both, so hopefully it's foolproof. */
      87             : 
      88             : #define FD_VM_SBPF_REJECT_RODATA_STACK_OVERLAP(v)  ( v != FD_SBPF_V0 )
      89             : #define FD_VM_SBPF_ENABLE_ELF_VADDR(v)             ( v != FD_SBPF_V0 )
      90             : /* SIMD-0166 */
      91   847481652 : #define FD_VM_SBPF_DYNAMIC_STACK_FRAMES(v)         ( v >= FD_SBPF_V1 )
      92             : /* SIMD-0173 */
      93      446004 : #define FD_VM_SBPF_CALLX_USES_SRC_REG(v)           ( v >= FD_SBPF_V2 )
      94             : #define FD_VM_SBPF_DISABLE_LDDW(v)                 ( v >= FD_SBPF_V2 )
      95      892008 : #define FD_VM_SBPF_ENABLE_LDDW(v)                  ( v <  FD_SBPF_V2 )
      96             : #define FD_VM_SBPF_DISABLE_LE(v)                   ( v >= FD_SBPF_V2 )
      97      446004 : #define FD_VM_SBPF_ENABLE_LE(v)                    ( v <  FD_SBPF_V2 )
      98    10704096 : #define FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(v)       ( v >= FD_SBPF_V2 )
      99             : /* SIMD-0174 */
     100    12042108 : #define FD_VM_SBPF_ENABLE_PQR(v)                   ( v >= FD_SBPF_V2 )
     101             : #define FD_VM_SBPF_DISABLE_NEG(v)                  ( v >= FD_SBPF_V2 )
     102      446004 : #define FD_VM_SBPF_ENABLE_NEG(v)                   ( v <  FD_SBPF_V2 )
     103      231306 : #define FD_VM_SBPF_SWAP_SUB_REG_IMM_OPERANDS(v)    ( v >= FD_SBPF_V2 )
     104      462612 : #define FD_VM_SBPF_EXPLICIT_SIGN_EXT(v)            ( v >= FD_SBPF_V2 )
     105             : /* SIMD-0178 + SIMD-0179 */
     106     1668363 : #define FD_VM_SBPF_STATIC_SYSCALLS(v)              ( v >= FD_SBPF_V3 )
     107             : /* SIMD-0189 */
     108             : #define FD_VM_SBPF_ENABLE_STRICTER_ELF_HEADERS(v)  ( v >= FD_SBPF_V3 )
     109             : #define FD_VM_SBPF_ENABLE_LOWER_BYTECODE_VADDR(v)  ( v >= FD_SBPF_V3 )
     110             : 
     111         372 : #define FD_VM_SBPF_DYNAMIC_STACK_FRAMES_ALIGN      (64U)
     112             : 
     113     1468470 : #define FD_VM_OFFSET_MASK (0xffffffffUL)
     114             : 
     115             : static const uint FD_VM_SBPF_STATIC_SYSCALLS_LIST[] = {
     116             :   0,
     117             :   //  1 = abort
     118             :   0xb6fc1a11,
     119             :   //  2 = sol_panic_
     120             :   0x686093bb,
     121             :   //  3 = sol_memcpy_
     122             :   0x717cc4a3,
     123             :   //  4 = sol_memmove_
     124             :   0x434371f8,
     125             :   //  5 = sol_memset_
     126             :   0x3770fb22,
     127             :   //  6 = sol_memcmp_
     128             :   0x5fdcde31,
     129             :   //  7 = sol_log_
     130             :   0x207559bd,
     131             :   //  8 = sol_log_64_
     132             :   0x5c2a3178,
     133             :   //  9 = sol_log_pubkey
     134             :   0x7ef088ca,
     135             :   // 10 = sol_log_compute_units_
     136             :   0x52ba5096,
     137             :   // 11 = sol_alloc_free_
     138             :   0x83f00e8f,
     139             :   // 12 = sol_invoke_signed_c
     140             :   0xa22b9c85,
     141             :   // 13 = sol_invoke_signed_rust
     142             :   0xd7449092,
     143             :   // 14 = sol_set_return_data
     144             :   0xa226d3eb,
     145             :   // 15 = sol_get_return_data
     146             :   0x5d2245e4,
     147             :   // 16 = sol_log_data
     148             :   0x7317b434,
     149             :   // 17 = sol_sha256
     150             :   0x11f49d86,
     151             :   // 18 = sol_keccak256
     152             :   0xd7793abb,
     153             :   // 19 = sol_secp256k1_recover
     154             :   0x17e40350,
     155             :   // 20 = sol_blake3
     156             :   0x174c5122,
     157             :   // 21 = sol_poseidon
     158             :   0xc4947c21,
     159             :   // 22 = sol_get_processed_sibling_instruction
     160             :   0xadb8efc8,
     161             :   // 23 = sol_get_stack_height
     162             :   0x85532d94,
     163             :   // 24 = sol_curve_validate_point
     164             :   0xaa2607ca,
     165             :   // 25 = sol_curve_group_op
     166             :   0xdd1c41a6,
     167             :   // 26 = sol_curve_multiscalar_mul
     168             :   0x60a40880,
     169             :   // 27 = sol_curve_pairing_map
     170             :   0xf111a47e,
     171             :   // 28 = sol_alt_bn128_group_op
     172             :   0xae0c318b,
     173             :   // 29 = sol_alt_bn128_compression
     174             :   0x334fd5ed,
     175             :   // 30 = sol_big_mod_exp
     176             :   0x780e4c15,
     177             :   // 31 = sol_remaining_compute_units
     178             :   0xedef5aee,
     179             :   // 32 = sol_create_program_address
     180             :   0x9377323c,
     181             :   // 33 = sol_try_find_program_address
     182             :   0x48504a38,
     183             :   // 34 = sol_get_sysvar
     184             :   0x13c1b505,
     185             :   // 35 = sol_get_epoch_stake
     186             :   0x5be92f4a,
     187             :   // 36 = sol_get_clock_sysvar
     188             :   0xd56b5fe9,
     189             :   // 37 = sol_get_epoch_schedule_sysvar
     190             :   0x23a29a61,
     191             :   // 38 = sol_get_last_restart_slot
     192             :   0x188a0031,
     193             :   // 39 = sol_get_epoch_rewards_sysvar
     194             :   0xfdba2b3b,
     195             :   // 40 = sol_get_fees_sysvar
     196             :   0x3b97b73c,
     197             :   // 41 = sol_get_rent_sysvar
     198             :   0xbf7188f6,
     199             : };
     200         126 : #define FD_VM_SBPF_STATIC_SYSCALLS_LIST_SZ (sizeof(FD_VM_SBPF_STATIC_SYSCALLS_LIST) / sizeof(uint))
     201             : 
     202             : FD_PROTOTYPES_BEGIN
     203             : 
     204             : /* Log error within the instr_ctx to match Agave/Rust error. */
     205             : 
     206        1248 : #define FD_VM_ERR_FOR_LOG_EBPF( vm, err ) (__extension__({                \
     207        1248 :     vm->instr_ctx->txn_ctx->exec_err = err;                               \
     208        1248 :     vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_EBPF;    \
     209        1248 :   }))
     210             : 
     211         351 : #define FD_VM_ERR_FOR_LOG_SYSCALL( vm, err ) (__extension__({             \
     212         351 :     vm->instr_ctx->txn_ctx->exec_err = err;                               \
     213         351 :     vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_SYSCALL; \
     214         351 :   }))
     215             : 
     216         501 : #define FD_VM_ERR_FOR_LOG_INSTR( vm, err ) (__extension__({               \
     217         501 :     vm->instr_ctx->txn_ctx->exec_err = err;                               \
     218         501 :     vm->instr_ctx->txn_ctx->exec_err_kind = FD_EXECUTOR_ERR_KIND_INSTR;   \
     219         501 :   }))
     220             : 
     221     1464162 : #define FD_VADDR_TO_REGION( _vaddr ) fd_ulong_min( (_vaddr) >> 32, 5UL )
     222             : 
     223             : /* fd_vm_instr APIs ***************************************************/
     224             : 
     225             : /* FIXME: MIGRATE FD_SBPF_INSTR_T STUFF TO THIS API */
     226             : 
     227             : /* fd_vm_instr returns the SBPF instruction word corresponding to the
     228             :    given fields. */
     229             : 
     230             : FD_FN_CONST static inline ulong
     231             : fd_vm_instr( ulong opcode, /* Assumed valid */
     232             :              ulong dst,    /* Assumed in [0,FD_VM_REG_CNT) */
     233             :              ulong src,    /* Assumed in [0,FD_VM_REG_CNT) */
     234             :              short offset,
     235       15957 :              uint  imm ) {
     236       15957 :   return opcode | (dst<<8) | (src<<12) | (((ulong)(ushort)offset)<<16) | (((ulong)imm)<<32);
     237       15957 : }
     238             : 
     239             : /* fd_vm_instr_* return the SBPF instruction field for the given word.
     240             :    fd_vm_instr_{normal,mem}_* only apply to {normal,mem} opclass
     241             :    instructions. */
     242             : 
     243     6034650 : FD_FN_CONST static inline ulong fd_vm_instr_opcode( ulong instr ) { return   instr      & 255UL;       } /* In [0,256) */
     244     6034650 : FD_FN_CONST static inline ulong fd_vm_instr_dst   ( ulong instr ) { return ((instr>> 8) &  15UL);      } /* In [0,16)  */
     245     6034650 : FD_FN_CONST static inline ulong fd_vm_instr_src   ( ulong instr ) { return ((instr>>12) &  15UL);      } /* In [0,16)  */
     246     6034650 : FD_FN_CONST static inline ulong fd_vm_instr_offset( ulong instr ) { return (ulong)(long)(short)(ushort)(instr>>16); }
     247     6118773 : FD_FN_CONST static inline uint  fd_vm_instr_imm   ( ulong instr ) { return (uint)(instr>>32);          }
     248             : 
     249           0 : FD_FN_CONST static inline ulong fd_vm_instr_opclass       ( ulong instr ) { return  instr      & 7UL; } /* In [0,8)  */
     250           0 : FD_FN_CONST static inline ulong fd_vm_instr_normal_opsrc  ( ulong instr ) { return (instr>>3) &  1UL; } /* In [0,2)  */
     251           0 : FD_FN_CONST static inline ulong fd_vm_instr_normal_opmode ( ulong instr ) { return (instr>>4) & 15UL; } /* In [0,16) */
     252           0 : FD_FN_CONST static inline ulong fd_vm_instr_mem_opsize    ( ulong instr ) { return (instr>>3) &  3UL; } /* In [0,4)  */
     253           0 : FD_FN_CONST static inline ulong fd_vm_instr_mem_opaddrmode( ulong instr ) { return (instr>>5) &  7UL; } /* In [0,16) */
     254             : 
     255             : /* fd_vm_mem API ******************************************************/
     256             : 
     257             : /* fd_vm_mem APIs support the fast mapping of virtual address ranges to
     258             :    host address ranges.  Since the SBPF virtual address space consists
     259             :    of 4 consecutive 4GiB regions and the mapable size of each region is
     260             :    less than 4 GiB (as implied by FD_VM_MEM_MAP_REGION_SZ==2^32-1 and
     261             :    that Solana protocol limits are much smaller still), it is impossible
     262             :    for a valid virtual address range to span multiple regions. */
     263             : 
     264             : /* fd_vm_mem_cfg configures the vm's tlb arrays.  Assumes vm is valid
     265             :    and vm already has configured the rodata, stack, heap and input
     266             :    regions.  Returns vm. */
     267             : 
     268             : static inline fd_vm_t *
     269      344277 : fd_vm_mem_cfg( fd_vm_t * vm ) {
     270      344277 :   vm->region_haddr[0] = 0UL;               vm->region_ld_sz[0] = (uint)0UL;             vm->region_st_sz[0] = (uint)0UL;
     271      344277 :   vm->region_haddr[1] = (ulong)vm->rodata; vm->region_ld_sz[1] = (uint)vm->rodata_sz;   vm->region_st_sz[1] = (uint)0UL;
     272      344277 :   vm->region_haddr[2] = (ulong)vm->stack;  vm->region_ld_sz[2] = (uint)FD_VM_STACK_MAX; vm->region_st_sz[2] = (uint)FD_VM_STACK_MAX;
     273      344277 :   vm->region_haddr[3] = (ulong)vm->heap;   vm->region_ld_sz[3] = (uint)vm->heap_max;    vm->region_st_sz[3] = (uint)vm->heap_max;
     274      344277 :   vm->region_haddr[5] = 0UL;               vm->region_ld_sz[5] = (uint)0UL;             vm->region_st_sz[5] = (uint)0UL;
     275      344277 :   if( FD_FEATURE_ACTIVE( vm->instr_ctx->slot_ctx, bpf_account_data_direct_mapping ) || !vm->input_mem_regions_cnt ) {
     276             :     /* When direct mapping is enabled, we don't use these fields because
     277             :        the load and stores are fragmented. */
     278      332118 :     vm->region_haddr[4] = 0UL; 
     279      332118 :     vm->region_ld_sz[4] = 0U; 
     280      332118 :     vm->region_st_sz[4] = 0U;
     281      332118 :   } else {
     282       12159 :     vm->region_haddr[4] = vm->input_mem_regions[0].haddr;  
     283       12159 :     vm->region_ld_sz[4] = vm->input_mem_regions[0].region_sz;    
     284       12159 :     vm->region_st_sz[4] = vm->input_mem_regions[0].region_sz;
     285       12159 :   }
     286      344277 :   return vm;
     287      344277 : }
     288             : 
     289             : /* fd_vm_mem_haddr translates the vaddr range [vaddr,vaddr+sz) (in
     290             :    infinite precision math) into the non-wrapping haddr range
     291             :    [haddr,haddr+sz).  On success, returns haddr and every byte in the
     292             :    haddr range is a valid address.  On failure, returns sentinel and
     293             :    there was at least one byte in the virtual address range that did not
     294             :    have a corresponding byte in the host address range.
     295             : 
     296             :    IMPORTANT SAFETY TIP!  When sz==0, the return value currently is
     297             :    arbitrary.  This is often fine as there should be no
     298             :    actual accesses to a sz==0 region.  However, this also means that
     299             :    testing return for sentinel is insufficient to tell if mapping
     300             :    failed.  That is, assuming sentinel is a location that could never
     301             :    happen on success:
     302             : 
     303             :      sz!=0 and ret!=sentinel -> success
     304             :      sz!=0 and ret==sentinel -> failure
     305             :      sz==0 -> ignore ret, application specific handling
     306             : 
     307             :    With ~O(2) extra fast branchless instructions, the below could be
     308             :    tweaked in the sz==0 case to return NULL or return a non-NULL
     309             :    sentinel value.  What is most optimal practically depends on how
     310             :    empty ranges and NULL vaddr handling is defined in the application.
     311             : 
     312             :    Requires ~O(10) fast branchless assembly instructions with 2 L1 cache
     313             :    hit loads and pretty good ILP.
     314             : 
     315             :    fd_vm_mem_haddr_fast is when the vaddr is for use when it is already
     316             :    known that the vaddr region has a valid mapping.
     317             :    
     318             :    These assumptions don't hold if direct mapping is enabled since input
     319             :    region lookups become O(log(n)). */
     320             : 
     321             : 
     322             : /* fd_vm_get_input_mem_region_idx returns the index into the input memory
     323             :    region array with the largest region offset that is <= the offset that
     324             :    is passed in.  This function makes NO guarantees about the input being
     325             :    a valid input region offset; the caller is responsible for safely handling
     326             :    it. */
     327             : static inline ulong
     328       95217 : fd_vm_get_input_mem_region_idx( fd_vm_t const * vm, ulong offset ) {
     329       95217 :   uint left  = 0U;
     330       95217 :   uint right = vm->input_mem_regions_cnt - 1U;
     331       95217 :   uint mid   = 0U;
     332             : 
     333      102801 :   while( left<right ) {
     334        7584 :     mid = (left+right) / 2U;
     335        7584 :     if( offset>=vm->input_mem_regions[ mid ].vaddr_offset+vm->input_mem_regions[ mid ].region_sz ) {
     336        2109 :       left = mid + 1U;
     337        5475 :     } else {
     338        5475 :       right = mid;
     339        5475 :     }
     340        7584 :   }
     341       95217 :   return left;
     342       95217 : }
     343             : 
     344             : /* fd_vm_find_input_mem_region returns the translated haddr for a given
     345             :    offset into the input region.  If an offset/sz is invalid or if an 
     346             :    illegal write is performed, the sentinel value is returned. If the offset
     347             :    provided is too large, it will choose the upper-most region as the
     348             :    region_idx. However, it will get caught for being too large of an access
     349             :    in the multi-region checks. */
     350             : static inline ulong
     351             : fd_vm_find_input_mem_region( fd_vm_t const * vm, 
     352             :                              ulong           offset,
     353             :                              ulong           sz, 
     354             :                              uchar           write,
     355             :                              ulong           sentinel,
     356       94665 :                              uchar *         is_multi_region ) {
     357       94665 :   if( FD_UNLIKELY( vm->input_mem_regions_cnt==0 ) ) {
     358           0 :     return sentinel; /* Access is too large */
     359           0 :   }
     360             : 
     361             :   /* Binary search to find the correct memory region.  If direct mapping is not
     362             :      enabled, then there is only 1 memory region which spans the input region. */
     363       94665 :   ulong region_idx = fd_vm_get_input_mem_region_idx( vm, offset );
     364             : 
     365       94665 :   ulong bytes_left          = sz;
     366       94665 :   ulong bytes_in_cur_region = fd_ulong_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     367       94665 :                                                 fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     368             : 
     369       94665 :   if( FD_UNLIKELY( write && vm->input_mem_regions[ region_idx ].is_writable==0U ) ) {
     370         126 :     return sentinel; /* Illegal write */
     371         126 :   }
     372             : 
     373       94539 :   ulong start_region_idx = region_idx;
     374             : 
     375       94539 :   *is_multi_region = 0;
     376       94608 :   while( FD_UNLIKELY( bytes_left>bytes_in_cur_region ) ) {
     377         825 :     *is_multi_region = 1;
     378         825 :     FD_LOG_DEBUG(( "Size of access spans multiple memory regions" ));
     379         825 :     if( FD_UNLIKELY( write && vm->input_mem_regions[ region_idx ].is_writable==0U ) ) {
     380           0 :       return sentinel; /* Illegal write */
     381           0 :     }
     382         825 :     bytes_left = fd_ulong_sat_sub( bytes_left, bytes_in_cur_region );
     383             : 
     384         825 :     region_idx += 1U;
     385             : 
     386         825 :     if( FD_UNLIKELY( region_idx==vm->input_mem_regions_cnt ) ) {
     387         756 :       return sentinel; /* Access is too large */
     388         756 :     }
     389          69 :     bytes_in_cur_region = vm->input_mem_regions[ region_idx ].region_sz;
     390          69 :   }
     391             : 
     392       93783 :   ulong adjusted_haddr = vm->input_mem_regions[ start_region_idx ].haddr + offset - vm->input_mem_regions[ start_region_idx ].vaddr_offset;
     393       93783 :   return adjusted_haddr; 
     394       94539 : }
     395             : 
     396             : 
     397             : static inline ulong
     398             : fd_vm_mem_haddr( fd_vm_t const *    vm,
     399             :                  ulong              vaddr,
     400             :                  ulong              sz,
     401             :                  ulong const *      vm_region_haddr, /* indexed [0,6) */
     402             :                  uint  const *      vm_region_sz,    /* indexed [0,6) */
     403             :                  uchar              write,           /* 1 if the access is a write, 0 if it is a read */
     404             :                  ulong              sentinel,
     405     1463010 :                  uchar *            is_multi_region ) {
     406     1463010 :   ulong region = FD_VADDR_TO_REGION( vaddr );
     407     1463010 :   ulong offset = vaddr & FD_VM_OFFSET_MASK;
     408             : 
     409             :   /* Stack memory regions have 4kB unmapped "gaps" in-between each frame (only if direct mapping is disabled).
     410             :     https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/memory_region.rs#L141
     411             :     */
     412     1463010 :   if ( FD_UNLIKELY( region == 2UL && !vm->direct_mapping ) ) {
     413             :     /* If an access starts in a gap region, that is an access violation */
     414      973026 :     if ( !!( vaddr & 0x1000 ) ) {
     415        1962 :       return sentinel;
     416        1962 :     }
     417             : 
     418             :     /* To account for the fact that we have gaps in the virtual address space but not in the 
     419             :        physical address space, we need to subtract from the offset the size of all the virtual
     420             :        gap frames underneath it.
     421             :        
     422             :        https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/memory_region.rs#L147-L149 */
     423      971064 :     ulong gap_mask = 0xFFFFFFFFFFFFF000;
     424      971064 :     offset = ( ( offset & gap_mask ) >> 1 ) | ( offset & ~gap_mask ); 
     425      971064 :   }
     426             : 
     427     1461048 :   ulong region_sz = (ulong)vm_region_sz[ region ];
     428     1461048 :   ulong sz_max    = region_sz - fd_ulong_min( offset, region_sz );
     429             : 
     430     1461048 :   if( region==4UL ) {
     431       94665 :     return fd_vm_find_input_mem_region( vm, offset, sz, write, sentinel, is_multi_region );
     432       94665 :   }
     433             :   
     434             : # ifdef FD_VM_INTERP_MEM_TRACING_ENABLED
     435             :   if ( FD_LIKELY( sz<=sz_max ) ) {
     436             :     fd_vm_trace_event_mem( vm->trace, write, vaddr, sz, vm_region_haddr[ region ] + offset );
     437             :   }
     438             : # endif
     439     1366383 :   return fd_ulong_if( sz<=sz_max, vm_region_haddr[ region ] + offset, sentinel );
     440     1461048 : }
     441             : 
     442             : FD_FN_PURE static inline ulong
     443             : fd_vm_mem_haddr_fast( fd_vm_t const * vm, 
     444             :                       ulong           vaddr,
     445          51 :                       ulong   const * vm_region_haddr ) { /* indexed [0,6) */
     446          51 :   uchar is_multi = 0;
     447          51 :   ulong region   = FD_VADDR_TO_REGION( vaddr );
     448          51 :   ulong offset   = vaddr & FD_VM_OFFSET_MASK;
     449          51 :   if( FD_UNLIKELY( region==4UL ) ) {
     450           0 :     return fd_vm_find_input_mem_region( vm, offset, 1UL, 0, 0UL, &is_multi );
     451           0 :   }
     452          51 :   return vm_region_haddr[ region ] + offset;
     453          51 : }
     454             : 
     455             : /* fd_vm_mem_ld_N loads N bytes from the host address location haddr,
     456             :    zero extends it to a ulong and returns the ulong.  haddr need not be
     457             :    aligned.  fd_vm_mem_ld_multi handles the case where the load spans 
     458             :    multiple input memory regions. */
     459             : 
     460          48 : static inline void fd_vm_mem_ld_multi( fd_vm_t const * vm, uint sz, ulong vaddr, ulong haddr, uchar * dst ) {
     461             : 
     462          48 :   ulong offset              = vaddr & FD_VM_OFFSET_MASK;
     463          48 :   ulong region_idx          = fd_vm_get_input_mem_region_idx( vm, offset );
     464          48 :   uint  bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     465          48 :                                               (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     466             : 
     467         264 :   while( sz-- ) {
     468         216 :     if( !bytes_in_cur_region ) {
     469          60 :       region_idx++;
     470          60 :       bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     471          60 :                                              (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     472          60 :       haddr               = vm->input_mem_regions[ region_idx ].haddr;
     473          60 :     }
     474             : 
     475         216 :     *dst++ = *(uchar *)haddr++;
     476         216 :     bytes_in_cur_region--;
     477         216 :   }
     478          48 : }
     479             : 
     480       97305 : FD_FN_PURE static inline ulong fd_vm_mem_ld_1( ulong haddr ) { 
     481       97305 :   return (ulong)*(uchar const *)haddr; 
     482       97305 : }
     483             : 
     484        2172 : FD_FN_PURE static inline ulong fd_vm_mem_ld_2( fd_vm_t const * vm, ulong vaddr, ulong haddr, uint is_multi_region ) { 
     485        2172 :   ushort t; 
     486        2172 :   if( FD_LIKELY( !is_multi_region ) ) {
     487        2160 :     memcpy( &t, (void const *)haddr, sizeof(ushort) ); 
     488        2160 :   } else {
     489          12 :     fd_vm_mem_ld_multi( vm, 2U, vaddr, haddr, (uchar *)&t );
     490          12 :   }
     491        2172 :   return (ulong)t;
     492        2172 : }
     493             : 
     494       19650 : FD_FN_PURE static inline ulong fd_vm_mem_ld_4( fd_vm_t const * vm, ulong vaddr, ulong haddr, uint is_multi_region ) {
     495       19650 :   uint t; 
     496       19650 :   if( FD_LIKELY( !is_multi_region ) ) {
     497       19626 :     memcpy( &t, (void const *)haddr, sizeof(uint) ); 
     498       19626 :   } else {
     499          24 :     fd_vm_mem_ld_multi( vm, 4U, vaddr, haddr, (uchar *)&t );
     500          24 :   }
     501       19650 :   return (ulong)t;
     502       19650 : }
     503             : 
     504      632106 : FD_FN_PURE static inline ulong fd_vm_mem_ld_8( fd_vm_t const * vm, ulong vaddr, ulong haddr, uint is_multi_region ) {
     505      632106 :   ulong t; 
     506      632106 :   if( FD_LIKELY( !is_multi_region ) ) {
     507      632094 :     memcpy( &t, (void const *)haddr, sizeof(ulong) ); 
     508      632094 :   } else {
     509          12 :     fd_vm_mem_ld_multi( vm, 8U, vaddr, haddr, (uchar *)&t );
     510          12 :   }
     511      632106 :   return t;
     512      632106 : }
     513             : 
     514             : /* fd_vm_mem_st_N stores val in little endian order to the host address
     515             :    location haddr.  haddr need not be aligned. fd_vm_mem_st_multi handles
     516             :    the case where the store spans multiple input memory regions. */
     517             : 
     518           0 : static inline void fd_vm_mem_st_multi( fd_vm_t const * vm, uint sz, ulong vaddr, ulong haddr, uchar * src ) {
     519           0 :   ulong   offset              = vaddr & FD_VM_OFFSET_MASK;
     520           0 :   ulong   region_idx          = fd_vm_get_input_mem_region_idx( vm, offset );
     521           0 :   ulong   bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     522           0 :                                                  (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     523           0 :   uchar * dst                 = (uchar*)haddr;
     524             : 
     525           0 :   while( sz-- ) {
     526           0 :     if( !bytes_in_cur_region ) {
     527           0 :       region_idx++;
     528           0 :       bytes_in_cur_region = fd_uint_sat_sub( vm->input_mem_regions[ region_idx ].region_sz,
     529           0 :                                              (uint)fd_ulong_sat_sub( offset, vm->input_mem_regions[ region_idx ].vaddr_offset ) );
     530           0 :       dst                 = (uchar *)vm->input_mem_regions[ region_idx ].haddr;
     531           0 :     }
     532             : 
     533           0 :     *dst++ = *src++;
     534           0 :     bytes_in_cur_region--;
     535           0 :   }
     536           0 : }
     537             : 
     538       65523 : static inline void fd_vm_mem_st_1( ulong haddr, uchar val ) { 
     539       65523 :   *(uchar *)haddr = val;
     540       65523 : }
     541             : 
     542             : static inline void fd_vm_mem_st_2( fd_vm_t const * vm,
     543             :                                    ulong           vaddr,
     544             :                                    ulong           haddr, 
     545             :                                    ushort          val, 
     546        2346 :                                    uint            is_multi_region ) { 
     547        2346 :   if( FD_LIKELY( !is_multi_region ) ) {
     548        2346 :     memcpy( (void *)haddr, &val, sizeof(ushort) ); 
     549        2346 :   } else {
     550           0 :     fd_vm_mem_st_multi( vm, 2U, vaddr, haddr, (uchar *)&val );
     551           0 :   }
     552        2346 : }
     553             : 
     554             : static inline void fd_vm_mem_st_4( fd_vm_t const * vm,
     555             :                                    ulong           vaddr,
     556             :                                    ulong           haddr, 
     557             :                                    uint            val, 
     558       15267 :                                    uint            is_multi_region ) { 
     559       15267 :   if( FD_LIKELY( !is_multi_region ) ) {
     560       15267 :     memcpy( (void *)haddr, &val, sizeof(uint)   ); 
     561       15267 :   } else {
     562           0 :     fd_vm_mem_st_multi( vm, 4U, vaddr, haddr, (uchar *)&val );
     563           0 :   }
     564       15267 : }
     565             : 
     566             : static inline void fd_vm_mem_st_8( fd_vm_t const * vm,
     567             :                                    ulong           vaddr,
     568             :                                    ulong           haddr,
     569             :                                    ulong           val,
     570      541626 :                                    uint            is_multi_region ) { 
     571      541626 :   if( FD_LIKELY( !is_multi_region ) ) {
     572      541626 :     memcpy( (void *)haddr, &val, sizeof(ulong)  ); 
     573      541626 :   } else {
     574           0 :     fd_vm_mem_st_multi( vm, 8U, vaddr, haddr, (uchar *)&val );
     575           0 :   }
     576      541626 : }
     577             : 
     578             : 
     579             : FD_PROTOTYPES_END
     580             : 
     581             : #endif /* HEADER_fd_src_flamenco_vm_fd_vm_private_h */

Generated by: LCOV version 1.14