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