1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef BERBERIS_RUNTIME_PRIMITIVES_MEMORY_REGION_RESERVATION_H_ 18 #define BERBERIS_RUNTIME_PRIMITIVES_MEMORY_REGION_RESERVATION_H_ 19 20 #include <climits> // CHAR_BITS 21 #include <cstdint> 22 #include <cstring> // memcpy 23 24 #include <atomic> 25 26 #include "berberis/guest_state/guest_addr.h" 27 #include "berberis/guest_state/guest_state_arch.h" 28 29 namespace berberis { 30 31 class MemoryRegionReservation { 32 public: 33 // Returns previously reserved address. Clear(CPUState * cpu)34 static GuestAddr Clear(CPUState* cpu) { 35 GuestAddr previous_address = cpu->reservation_address; 36 cpu->reservation_address = kNullGuestAddr; 37 return previous_address; 38 } 39 40 template <typename Type> Load(CPUState * cpu,GuestAddr addr,std::memory_order mem_order)41 static Type Load(CPUState* cpu, GuestAddr addr, std::memory_order mem_order) { 42 static_assert(sizeof(Type) <= sizeof(cpu->reservation_value), 43 "Type is too big for reservation"); 44 45 GuestAddr aligned_addr = addr - (addr % sizeof(Reservation)); 46 cpu->reservation_address = aligned_addr; 47 48 cpu->reservation_value = ReservationLoad(cpu, aligned_addr, mem_order); 49 50 // Extract the result from the region. 51 return static_cast<Type>(cpu->reservation_value >> ((addr - aligned_addr) * CHAR_BIT)); 52 } 53 54 // Returns 0 on successful store or 1 otherwise. 55 template <typename Type> Store(CPUState * cpu,GuestAddr addr,Type value,std::memory_order mem_order)56 static uint32_t Store(CPUState* cpu, GuestAddr addr, Type value, std::memory_order mem_order) { 57 static_assert(sizeof(Type) <= sizeof(cpu->reservation_value), 58 "Type is too big for reservation"); 59 60 GuestAddr reservation_address = Clear(cpu); 61 62 GuestAddr aligned_addr = addr - (addr % sizeof(Reservation)); 63 if (aligned_addr != reservation_address) { 64 return 1; 65 } 66 67 auto cur_value = cpu->reservation_value; 68 auto new_value = cpu->reservation_value; 69 70 // Embed value into new region value. 71 memcpy(reinterpret_cast<char*>(&new_value) + (addr - aligned_addr), &value, sizeof(Type)); 72 73 return ReservationExchange(cpu, aligned_addr, cur_value, new_value, mem_order) ? 0 : 1; 74 } 75 76 using Entry = std::atomic<void*>; 77 78 static void SetOwner(GuestAddr aligned_addr, void* cpu); 79 static Entry* TryLock(GuestAddr aligned_addr, void* cpu); 80 static void Unlock(Entry* entry); 81 82 private: 83 static Reservation ReservationLoad(void* cpu, 84 GuestAddr aligned_addr, 85 std::memory_order mem_order); 86 static bool ReservationExchange(void* cpu, 87 GuestAddr aligned_addr, 88 Reservation expected, 89 Reservation value, 90 std::memory_order mem_order); 91 }; 92 93 } // namespace berberis 94 95 #endif // BERBERIS_RUNTIME_PRIMITIVES_MEMORY_REGION_RESERVATION_H_ 96