1 //===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  Created by Greg Clayton on 6/26/07.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "MachVMMemory.h"
15 #include "MachVMRegion.h"
16 #include "DNBLog.h"
17 #include <mach/mach_vm.h>
18 
19 MachVMMemory::MachVMMemory() :
20     m_page_size    (kInvalidPageSize),
21     m_err        (0)
22 {
23 }
24 
25 MachVMMemory::~MachVMMemory()
26 {
27 }
28 
29 nub_size_t
30 MachVMMemory::PageSize()
31 {
32     if (m_page_size == kInvalidPageSize)
33     {
34         m_err = ::host_page_size( ::mach_host_self(), &m_page_size);
35         if (m_err.Fail())
36             m_page_size = 0;
37     }
38     return m_page_size;
39 }
40 
41 nub_size_t
42 MachVMMemory::MaxBytesLeftInPage(nub_addr_t addr, nub_size_t count)
43 {
44     const nub_size_t page_size = PageSize();
45     if (page_size > 0)
46     {
47         nub_size_t page_offset = (addr % page_size);
48         nub_size_t bytes_left_in_page = page_size - page_offset;
49         if (count > bytes_left_in_page)
50             count = bytes_left_in_page;
51     }
52     return count;
53 }
54 
55 nub_size_t
56 MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count)
57 {
58     if (data == NULL || data_count == 0)
59         return 0;
60 
61     nub_size_t total_bytes_read = 0;
62     nub_addr_t curr_addr = address;
63     uint8_t *curr_data = (uint8_t*)data;
64     while (total_bytes_read < data_count)
65     {
66         mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read);
67         mach_msg_type_number_t curr_bytes_read = 0;
68         vm_offset_t vm_memory = NULL;
69         m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
70         if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
71             m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i )", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read);
72 
73         if (m_err.Success())
74         {
75             if (curr_bytes_read != curr_size)
76             {
77                 if (DNBLogCheckLogBit(LOG_MEMORY))
78                     m_err.LogThreaded("::mach_vm_read ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i ) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size);
79             }
80             ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
81             ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
82             total_bytes_read += curr_bytes_read;
83             curr_addr += curr_bytes_read;
84             curr_data += curr_bytes_read;
85         }
86         else
87         {
88             break;
89         }
90     }
91     return total_bytes_read;
92 }
93 
94 
95 nub_size_t
96 MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count)
97 {
98     MachVMRegion vmRegion(task);
99 
100     nub_size_t total_bytes_written = 0;
101     nub_addr_t curr_addr = address;
102     const uint8_t *curr_data = (const uint8_t*)data;
103 
104 
105     while (total_bytes_written < data_count)
106     {
107         if (vmRegion.GetRegionForAddress(curr_addr))
108         {
109             mach_vm_size_t curr_data_count = data_count - total_bytes_written;
110             mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
111             if (region_bytes_left == 0)
112             {
113                 break;
114             }
115             if (curr_data_count > region_bytes_left)
116                 curr_data_count = region_bytes_left;
117 
118             if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
119             {
120                 nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count);
121                 if (bytes_written <= 0)
122                 {
123                     // Error should have already be posted by WriteRegion...
124                     break;
125                 }
126                 else
127                 {
128                     total_bytes_written += bytes_written;
129                     curr_addr += bytes_written;
130                     curr_data += bytes_written;
131                 }
132             }
133             else
134             {
135                 DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count));
136                 break;
137             }
138         }
139         else
140         {
141             DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
142             break;
143         }
144     }
145 
146     return total_bytes_written;
147 }
148 
149 
150 nub_size_t
151 MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count)
152 {
153     if (data == NULL || data_count == 0)
154         return 0;
155 
156     nub_size_t total_bytes_written = 0;
157     nub_addr_t curr_addr = address;
158     const uint8_t *curr_data = (const uint8_t*)data;
159     while (total_bytes_written < data_count)
160     {
161         mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written);
162         m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
163         if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
164             m_err.LogThreaded("::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count);
165 
166 #if !defined (__i386__) && !defined (__x86_64__)
167         vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
168 
169         m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
170         if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
171             m_err.LogThreaded("::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count);
172 #endif
173 
174         if (m_err.Success())
175         {
176             total_bytes_written += curr_data_count;
177             curr_addr += curr_data_count;
178             curr_data += curr_data_count;
179         }
180         else
181         {
182             break;
183         }
184     }
185     return total_bytes_written;
186 }
187