FreeNOS
ARMFirstTable.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Niek Linnenbank
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <FreeNOS/System.h>
19 #include <SplitAllocator.h>
20 #include <MemoryBlock.h>
21 #include "ARMCore.h"
22 #include "ARMConstant.h"
23 #include "ARMFirstTable.h"
24 
32 #define PAGE1_NONE 0
33 #define PAGE1_TABLE (1 << 0)
34 #define PAGE1_SECTION (1 << 1)
35 
55 #define PAGE1_UNCACHED (PAGE1_TEX)
56 
58 #define PAGE1_CACHE_WRITEBACK (PAGE1_TEX | PAGE1_CACHE | PAGE1_BUFFER)
59 
61 #define PAGE1_CACHE_WRITETHROUGH (PAGE1_CACHE)
62 
64 #define PAGE1_DEVICE_PRIV ((1 << 13))
65 
67 #define PAGE1_DEVICE_SHARED (PAGE1_BUFFER)
68 
69 #define PAGE1_TEX (1 << 12)
70 #define PAGE1_CACHE (1 << 3)
71 #define PAGE1_BUFFER (1 << 2)
72 #define PAGE1_SHARED (1 << 16)
73 
84 #define PAGE1_NOEXEC (1 << 4)
85 
86 /* Read-only flag */
87 #ifdef ARMV7
88 #define PAGE1_APX (1 << 15)
89 #else
90 #define PAGE1_APX (1 << 9)
91 #endif
92 
93 /* User access permissions flag */
94 #define PAGE1_AP_USER (1 << 11)
95 
96 /* System access permissions flag */
97 #define PAGE1_AP_SYS (1 << 10)
98 
110 #define DIRENTRY(vaddr) \
111  ((vaddr) >> DIRSHIFT)
112 
114 {
115  u32 entry = m_tables[ DIRENTRY(virt) ];
116 
117  // Check if the page table is present.
118  if (!(entry & PAGE1_TABLE))
119  return ZERO;
120  else
121  return (ARMSecondTable *) alloc->toVirtual(entry & PAGEMASK);
122 }
123 
125  Address phys,
126  Memory::Access access,
127  SplitAllocator *alloc)
128 {
129  ARMSecondTable *table = getSecondTable(virt, alloc);
130  Arch::Cache cache;
131  Allocator::Range allocPhys, allocVirt;
132 
133  // Check if the page table is present.
134  if (!table)
135  {
136  // Reject if already mapped as a (super)section
137  if (m_tables[ DIRENTRY(virt) ] & PAGE1_SECTION)
139 
140  // Allocate a new page table
141  allocPhys.address = 0;
142  allocPhys.size = sizeof(ARMSecondTable);
143  allocPhys.alignment = PAGESIZE;
144 
145  if (alloc->allocate(allocPhys, allocVirt) != Allocator::Success)
147 
148  MemoryBlock::set((void *)allocVirt.address, 0, PAGESIZE);
149 
150  // Assign to the page directory. Do not assign permission flags (only for direct sections).
151  m_tables[ DIRENTRY(virt) ] = allocPhys.address | PAGE1_TABLE;
152  cache.cleanData(&m_tables[DIRENTRY(virt)]);
153  table = getSecondTable(virt, alloc);
154  }
155  return table->map(virt, phys, access);
156 }
157 
159  SplitAllocator *alloc)
160 {
161  Arch::Cache cache;
162 
163  if (range.size & 0xfffff)
165 
166  if ((range.phys & ~PAGEMASK) || (range.virt & ~PAGEMASK))
168 
169  for (Size i = 0; i < range.size; i += MegaByte(1))
170  {
171  if (m_tables[ DIRENTRY(range.virt + i) ] & (PAGE1_TABLE | PAGE1_SECTION))
173 
174  m_tables[ DIRENTRY(range.virt + i) ] = (range.phys + i) | PAGE1_SECTION | flags(range.access);
175  cache.cleanData(&m_tables[DIRENTRY(range.virt + i)]);
176  }
177  return MemoryContext::Success;
178 }
179 
181 {
182  ARMSecondTable *table = getSecondTable(virt, alloc);
183  Arch::Cache cache;
184 
185  if (!table)
186  {
187  if (m_tables[DIRENTRY(virt)] & PAGE1_SECTION)
188  {
189  m_tables[DIRENTRY(virt)] = PAGE1_NONE;
190  cache.cleanData(&m_tables[DIRENTRY(virt)]);
191  return MemoryContext::Success;
192  }
193  else
195  }
196  else
197  return table->unmap(virt);
198 }
199 
201  Address *phys,
202  SplitAllocator *alloc) const
203 {
204  ARMSecondTable *table = getSecondTable(virt, alloc);
205  if (!table)
206  {
207  if (m_tables[DIRENTRY(virt)] & PAGE1_SECTION)
208  {
209  const Address offsetInSection = virt % MegaByte(1);
210 
211  *phys = (m_tables[DIRENTRY(virt)] & SECTIONMASK) +
212  ((offsetInSection / PAGESIZE) * PAGESIZE);
213  return MemoryContext::Success;
214  }
216  }
217  else
218  return table->translate(virt, phys);
219 }
220 
222  Memory::Access *access,
223  SplitAllocator *alloc) const
224 {
225  ARMSecondTable *table = getSecondTable(virt, alloc);
226  if (!table)
228  else
229  return table->access(virt, access);
230 }
231 
233 {
234  u32 f = PAGE1_AP_SYS;
235 
236  // Permissions
237  if (!(access & Memory::Executable)) f |= PAGE1_NOEXEC;
238  if ((access & Memory::User)) f |= PAGE1_AP_USER;
239  if (!(access & Memory::Writable)) f |= PAGE1_APX;
240 
241  // Caching
243  else if (access & Memory::Uncached) f |= PAGE1_UNCACHED;
244  else f |= PAGE1_CACHE_WRITEBACK;
245 
246  return f;
247 }
248 
250  const Address phys)
251 {
252  // Some pages that are part of the boot core's memory region
253  // are mapped on secondary cores. They can't be released there.
254  const Address allocBase = alloc->base();
255  const Size allocSize = alloc->size();
256  if (phys < allocBase || phys > allocBase + allocSize)
257  {
258  return;
259  }
260 
261  // Note that some pages may have double mappings.
262  // Avoid attempting to release the same page twice or more.
263  if (alloc->isAllocated(phys))
264  {
265  alloc->release(phys);
266  }
267 }
268 
270  SplitAllocator *alloc)
271 {
272  Address phys;
273 
274  // Walk the full range of memory specified
275  for (Size addr = range.virt; addr < range.virt + range.size; addr += PAGESIZE)
276  {
277  ARMSecondTable *table = getSecondTable(addr, alloc);
278  if (table == ZERO)
279  {
281  }
282 
283  if (table->translate(addr, &phys) != MemoryContext::Success)
284  {
286  }
287 
288  releasePhysical(alloc, phys);
289  table->unmap(addr);
290  }
291 
292  return MemoryContext::Success;
293 }
294 
296  SplitAllocator *alloc,
297  const bool tablesOnly)
298 {
299  Address phys;
300 
301  // Input must be aligned to section address
302  if (range.virt & ~SECTIONMASK)
303  {
305  }
306 
307  // Walk the page directory
308  for (Size addr = range.virt; addr < range.virt + range.size; addr += MegaByte(1))
309  {
310  ARMSecondTable *table = getSecondTable(addr, alloc);
311  if (!table)
312  {
313  continue;
314  }
315 
316  // Release mapped pages, if requested
317  if (!tablesOnly)
318  {
319  for (Size i = 0; i < MegaByte(1); i += PAGESIZE)
320  {
321  if (table->translate(i, &phys) == MemoryContext::Success)
322  {
323  releasePhysical(alloc, phys);
324  }
325  }
326  }
327  // Release page table
328  alloc->release(m_tables[ DIRENTRY(addr) ] & PAGEMASK);
329  m_tables[ DIRENTRY(addr) ] = 0;
330  }
331 
332  return MemoryContext::Success;
333 }
SplitAllocator::allocate
virtual Result allocate(Range &args)
Allocate physical memory.
Definition: SplitAllocator.cpp:35
ARMFirstTable::getSecondTable
ARMSecondTable * getSecondTable(Address virt, SplitAllocator *alloc) const
Retrieve second level page table.
Definition: ARMFirstTable.cpp:113
SECTIONMASK
#define SECTIONMASK
Mask for large 1MiB section mappings.
Definition: ARMConstant.h:124
Memory::Range
Memory range.
Definition: Memory.h:55
SplitAllocator.h
Memory::Executable
@ Executable
Definition: Memory.h:43
PAGE1_TABLE
#define PAGE1_TABLE
Definition: ARMFirstTable.cpp:33
ARMConstant.h
ARMFirstTable::map
MemoryContext::Result map(Address virt, Address phys, Memory::Access access, SplitAllocator *alloc)
Map a virtual address to a physical address.
Definition: ARMFirstTable.cpp:124
ARMFirstTable::translate
MemoryContext::Result translate(Address virt, Address *phys, SplitAllocator *alloc) const
Translate virtual address to physical address.
Definition: ARMFirstTable.cpp:200
PAGE1_UNCACHED
#define PAGE1_UNCACHED
Disable all caching.
Definition: ARMFirstTable.cpp:55
MemoryBlock::set
static void * set(void *dest, int ch, unsigned count)
Fill memory with a constant byte.
Definition: MemoryBlock.cpp:25
PAGE1_NONE
#define PAGE1_NONE
Definition: ARMFirstTable.cpp:32
Memory::Writable
@ Writable
Definition: Memory.h:42
PAGEMASK
#define PAGEMASK
Mask to find the page.
Definition: ARMConstant.h:121
Memory::User
@ User
Definition: Memory.h:44
ARMSecondTable::access
MemoryContext::Result access(Address virt, Memory::Access *access) const
Get Access flags for a virtual address.
Definition: ARMSecondTable.cpp:141
PAGESIZE
#define PAGESIZE
ARM uses 4K pages.
Definition: ARMConstant.h:97
MemoryContext::AlreadyExists
@ AlreadyExists
Definition: MemoryContext.h:54
ARMFirstTable::m_tables
u32 m_tables[4096]
Array of page table entries.
Definition: ARMFirstTable.h:170
ARMCore.h
Memory::Device
@ Device
Definition: Memory.h:48
Address
unsigned long Address
A memory address.
Definition: Types.h:131
ARMFirstTable.h
ARMFirstTable::mapLarge
MemoryContext::Result mapLarge(Memory::Range range, SplitAllocator *alloc)
Map a contigous range of virtual memory to physical memory.
Definition: ARMFirstTable.cpp:158
Allocator::Range::address
Address address
Starting address of the memory range.
Definition: Allocator.h:67
MemoryBlock.h
PAGE1_AP_USER
#define PAGE1_AP_USER
Definition: ARMFirstTable.cpp:94
Allocator::Range::alignment
Size alignment
Alignment in bytes or ZERO for default alignment.
Definition: Allocator.h:69
MemoryContext::InvalidAddress
@ InvalidAddress
Definition: MemoryContext.h:52
DIRENTRY
#define DIRENTRY(vaddr)
Entry inside the page directory of a given virtual address.
Definition: ARMFirstTable.cpp:110
ARMFirstTable::flags
u32 flags(Memory::Access access) const
Convert Memory::Access to first level page table flags.
Definition: ARMFirstTable.cpp:232
Allocator::Range::size
Size size
Amount of memory in bytes.
Definition: Allocator.h:68
SplitAllocator
Allocator which separates kernel mapped memory at virtual and physical addresses.
Definition: SplitAllocator.h:37
MegaByte
#define MegaByte(v)
Convert megabytes to bytes.
Definition: Macros.h:57
ARMSecondTable::unmap
MemoryContext::Result unmap(Address virt)
Remove virtual address mapping.
Definition: ARMSecondTable.cpp:123
ARMFirstTable::releasePhysical
void releasePhysical(SplitAllocator *alloc, const Address phys)
Release a single physical page.
Definition: ARMFirstTable.cpp:249
Allocator::Success
@ Success
Definition: Allocator.h:55
Memory::Range::phys
Address phys
Physical address.
Definition: Memory.h:58
Memory::Uncached
@ Uncached
Definition: Memory.h:45
MemoryContext::Result
Result
Result codes.
Definition: MemoryContext.h:49
ARMCacheV6
ARMv6 cache management implementation.
Definition: ARMCacheV6.h:42
MemoryContext::InvalidSize
@ InvalidSize
Definition: MemoryContext.h:53
ARMSecondTable
ARM second level page table implementation.
Definition: ARMSecondTable.h:39
u32
unsigned int u32
Unsigned 32-bit number.
Definition: Types.h:53
ARMFirstTable::access
MemoryContext::Result access(Address virt, Memory::Access *access, SplitAllocator *alloc) const
Get Access flags for a virtual address.
Definition: ARMFirstTable.cpp:221
MemoryContext::Success
@ Success
Definition: MemoryContext.h:51
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
Cache::cleanData
virtual Result cleanData(Address addr)
Clean one data page.
Definition: Cache.cpp:20
Allocator::size
virtual Size size() const
Get memory size.
Definition: Allocator.cpp:64
ARMFirstTable::releaseRange
MemoryContext::Result releaseRange(const Memory::Range range, SplitAllocator *alloc)
Release range of memory.
Definition: ARMFirstTable.cpp:269
Memory::Access
Access
Memory access flags.
Definition: Memory.h:38
Allocator::base
Address base() const
Get memory base address for allocations.
Definition: Allocator.cpp:69
PAGE1_APX
#define PAGE1_APX
Definition: ARMFirstTable.cpp:90
MemoryContext::OutOfMemory
@ OutOfMemory
Definition: MemoryContext.h:55
ARMFirstTable::releaseSection
MemoryContext::Result releaseSection(const Memory::Range range, SplitAllocator *alloc, const bool tablesOnly)
Release memory sections.
Definition: ARMFirstTable.cpp:295
ARMSecondTable::translate
MemoryContext::Result translate(Address virt, Address *phys) const
Translate virtual address to physical address.
Definition: ARMSecondTable.cpp:132
Allocator::Range
Describes a range of memory.
Definition: Allocator.h:65
PAGE1_CACHE_WRITEBACK
#define PAGE1_CACHE_WRITEBACK
Outer and Inner Write-Back.
Definition: ARMFirstTable.cpp:58
entry
u32 entry[]
Definition: IntelACPI.h:64
SplitAllocator::release
virtual Result release(const Address addr)
Release memory page.
Definition: SplitAllocator.cpp:89
PAGE1_DEVICE_SHARED
#define PAGE1_DEVICE_SHARED
Memory Mapped Device (Shared)
Definition: ARMFirstTable.cpp:67
Memory::Range::virt
Address virt
Virtual address.
Definition: Memory.h:57
PAGE1_AP_SYS
#define PAGE1_AP_SYS
Definition: ARMFirstTable.cpp:97
ARMSecondTable::map
MemoryContext::Result map(Address virt, Address phys, Memory::Access)
Map a virtual address to a physical address.
Definition: ARMSecondTable.cpp:107
Memory::Range::size
Size size
Size in number of bytes.
Definition: Memory.h:59
Memory::Range::access
Access access
Page access flags.
Definition: Memory.h:60
ZERO
#define ZERO
Zero value.
Definition: Macros.h:43
SplitAllocator::isAllocated
bool isAllocated(const Address page) const
Check if a physical page is allocated.
Definition: SplitAllocator.cpp:106
PAGE1_NOEXEC
#define PAGE1_NOEXEC
No-execution flag.
Definition: ARMFirstTable.cpp:84
PAGE1_SECTION
#define PAGE1_SECTION
Definition: ARMFirstTable.cpp:34
SplitAllocator::toVirtual
Address toVirtual(const Address phys) const
Convert Address to virtual pointer.
Definition: SplitAllocator.cpp:94
ARMFirstTable::unmap
MemoryContext::Result unmap(Address virt, SplitAllocator *alloc)
Remove virtual address mapping.
Definition: ARMFirstTable.cpp:180