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 int
56 MachVMMemory::MemoryRegionInfo(task_t task, nub_addr_t address, char *outbuf, nub_size_t outbufsize)
57 {
58     MachVMRegion vmRegion(task);
59     outbuf[0] = '\0';
60 
61     if (vmRegion.GetRegionForAddress(address) && vmRegion.GetRegionDescription(outbuf, outbufsize))
62         return 1;
63     else
64         return 0;
65 }
66 
67 nub_size_t
68 MachVMMemory::Read(task_t task, nub_addr_t address, void *data, nub_size_t data_count)
69 {
70     if (data == NULL || data_count == 0)
71         return 0;
72 
73     nub_size_t total_bytes_read = 0;
74     nub_addr_t curr_addr = address;
75     uint8_t *curr_data = (uint8_t*)data;
76     while (total_bytes_read < data_count)
77     {
78         mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read);
79         mach_msg_type_number_t curr_bytes_read = 0;
80         vm_offset_t vm_memory = NULL;
81         m_err = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
82         if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
83             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);
84 
85         if (m_err.Success())
86         {
87             if (curr_bytes_read != curr_size)
88             {
89                 if (DNBLogCheckLogBit(LOG_MEMORY))
90                     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);
91             }
92             ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
93             ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
94             total_bytes_read += curr_bytes_read;
95             curr_addr += curr_bytes_read;
96             curr_data += curr_bytes_read;
97         }
98         else
99         {
100             break;
101         }
102     }
103     return total_bytes_read;
104 }
105 
106 
107 nub_size_t
108 MachVMMemory::Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count)
109 {
110     MachVMRegion vmRegion(task);
111 
112     nub_size_t total_bytes_written = 0;
113     nub_addr_t curr_addr = address;
114     const uint8_t *curr_data = (const uint8_t*)data;
115 
116 
117     while (total_bytes_written < data_count)
118     {
119         if (vmRegion.GetRegionForAddress(curr_addr))
120         {
121             mach_vm_size_t curr_data_count = data_count - total_bytes_written;
122             mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
123             if (region_bytes_left == 0)
124             {
125                 break;
126             }
127             if (curr_data_count > region_bytes_left)
128                 curr_data_count = region_bytes_left;
129 
130             if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
131             {
132                 nub_size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count);
133                 if (bytes_written <= 0)
134                 {
135                     // Error should have already be posted by WriteRegion...
136                     break;
137                 }
138                 else
139                 {
140                     total_bytes_written += bytes_written;
141                     curr_addr += bytes_written;
142                     curr_data += bytes_written;
143                 }
144             }
145             else
146             {
147                 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));
148                 break;
149             }
150         }
151         else
152         {
153             DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
154             break;
155         }
156     }
157 
158     return total_bytes_written;
159 }
160 
161 
162 nub_size_t
163 MachVMMemory::WriteRegion(task_t task, const nub_addr_t address, const void *data, const nub_size_t data_count)
164 {
165     if (data == NULL || data_count == 0)
166         return 0;
167 
168     nub_size_t total_bytes_written = 0;
169     nub_addr_t curr_addr = address;
170     const uint8_t *curr_data = (const uint8_t*)data;
171     while (total_bytes_written < data_count)
172     {
173         mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written);
174         m_err = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
175         if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
176             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);
177 
178 #if !defined (__i386__) && !defined (__x86_64__)
179         vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
180 
181         m_err = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
182         if (DNBLogCheckLogBit(LOG_MEMORY) || m_err.Fail())
183             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);
184 #endif
185 
186         if (m_err.Success())
187         {
188             total_bytes_written += curr_data_count;
189             curr_addr += curr_data_count;
190             curr_data += curr_data_count;
191         }
192         else
193         {
194             break;
195         }
196     }
197     return total_bytes_written;
198 }
199