Comparar commits
3 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 619f6c0b12 | |||
| 4f9da73631 | |||
| 6f899c90a9 |
@@ -15,6 +15,7 @@
|
||||
#include <arch/arm64/exceptions.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/stats.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/fault.h>
|
||||
#include <platform.h>
|
||||
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <magenta/compiler.h>
|
||||
#include <list.h>
|
||||
#include <arch/arm64/mmu.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
|
||||
#define ARCH_ASPACE_MAGIC 0x41524153 // ARAS
|
||||
|
||||
@@ -32,5 +29,28 @@ struct arch_aspace {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
__END_CDECLS
|
||||
class ARM64ArchVmAspace final : public ArchVmAspaceBase {
|
||||
public:
|
||||
ARM64ArchVmAspace();
|
||||
virtual ~ARM64ArchVmAspace();
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(ARM64ArchVmAspace);
|
||||
|
||||
virtual status_t Init(vaddr_t base, size_t size, uint mmu_flags);
|
||||
virtual status_t Destroy();
|
||||
|
||||
// main methods
|
||||
virtual status_t Map(vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped);
|
||||
virtual status_t Unmap(vaddr_t vaddr, size_t count, size_t* unmapped);
|
||||
virtual status_t Protect(vaddr_t vaddr, size_t count, uint mmu_flags);
|
||||
virtual status_t Query(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags);
|
||||
|
||||
static void ContextSwitch(ARM64ArchVmAspace *from, ARM64ArchVmAspace *to);
|
||||
|
||||
arch_aspace& GetInnerAspace() { return aspace_; }
|
||||
|
||||
private:
|
||||
arch_aspace aspace_ = {};
|
||||
};
|
||||
|
||||
using ArchVmAspace = ARM64ArchVmAspace;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include <arch/mmu.h>
|
||||
#include <arch/arm64/mmu.h>
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
#include <kernel/vm/pmm.h>
|
||||
#include <lib/heap.h>
|
||||
#include <rand.h>
|
||||
@@ -123,7 +125,7 @@ static pte_t mmu_flags_to_pte_attr(uint flags) {
|
||||
return attr;
|
||||
}
|
||||
|
||||
status_t arch_mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr, uint* flags) {
|
||||
status_t arch_internal::arch_mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr, uint* flags) {
|
||||
ulong index;
|
||||
uint index_shift;
|
||||
uint page_size_shift;
|
||||
@@ -655,7 +657,7 @@ static status_t arm64_mmu_protect(vaddr_t vaddr, size_t size, pte_t attrs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
status_t arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, const size_t count, uint flags, size_t* mapped) {
|
||||
status_t arch_internal::arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, const size_t count, uint flags, size_t* mapped) {
|
||||
LTRACEF("vaddr %#" PRIxPTR " paddr %#" PRIxPTR " count %zu flags %#x\n",
|
||||
vaddr, paddr, count, flags);
|
||||
|
||||
@@ -702,7 +704,7 @@ status_t arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, const
|
||||
return (ret < 0) ? (status_t)ret : NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, const size_t count, size_t* unmapped) {
|
||||
status_t arch_internal::arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, const size_t count, size_t* unmapped) {
|
||||
LTRACEF("vaddr %#" PRIxPTR " count %zu\n", vaddr, count);
|
||||
|
||||
DEBUG_ASSERT(aspace);
|
||||
@@ -741,7 +743,7 @@ status_t arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, const size_t count
|
||||
return (ret < 0) ? (status_t)ret : 0;
|
||||
}
|
||||
|
||||
status_t arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, uint flags) {
|
||||
status_t arch_internal::arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, uint flags) {
|
||||
DEBUG_ASSERT(aspace);
|
||||
DEBUG_ASSERT(aspace->magic == ARCH_ASPACE_MAGIC);
|
||||
|
||||
@@ -774,7 +776,7 @@ status_t arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, ui
|
||||
return ret;
|
||||
}
|
||||
|
||||
status_t arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size, uint flags) {
|
||||
status_t arch_internal::arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size, uint flags) {
|
||||
LTRACEF("aspace %p, base %#" PRIxPTR ", size 0x%zx, flags 0x%x\n",
|
||||
aspace, base, size, flags);
|
||||
|
||||
@@ -826,7 +828,7 @@ status_t arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size,
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_destroy_aspace(arch_aspace_t* aspace) {
|
||||
status_t arch_internal::arch_mmu_destroy_aspace(arch_aspace_t* aspace) {
|
||||
LTRACEF("aspace %p\n", aspace);
|
||||
|
||||
DEBUG_ASSERT(aspace);
|
||||
@@ -849,7 +851,7 @@ status_t arch_mmu_destroy_aspace(arch_aspace_t* aspace) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void arch_mmu_context_switch(arch_aspace_t* old_aspace, arch_aspace_t* aspace) {
|
||||
void arch_internal::arch_mmu_context_switch(arch_aspace_t* old_aspace, arch_aspace_t* aspace) {
|
||||
if (TRACE_CONTEXT_SWITCH)
|
||||
TRACEF("aspace %p\n", aspace);
|
||||
|
||||
@@ -887,3 +889,38 @@ void arch_zero_page(void* _ptr) {
|
||||
ptr += zva_size;
|
||||
} while (ptr != end_ptr);
|
||||
}
|
||||
|
||||
|
||||
ARM64ArchVmAspace::ARM64ArchVmAspace() { }
|
||||
|
||||
ARM64ArchVmAspace::~ARM64ArchVmAspace() {
|
||||
// TODO: check that we've destroyed the aspace
|
||||
}
|
||||
|
||||
status_t ARM64ArchVmAspace::Init(vaddr_t base, size_t size, uint mmu_flags) {
|
||||
return arch_internal::arch_mmu_init_aspace(&aspace_, base, size, mmu_flags);
|
||||
}
|
||||
|
||||
status_t ARM64ArchVmAspace::Destroy() {
|
||||
return arch_internal::arch_mmu_destroy_aspace(&aspace_);
|
||||
}
|
||||
|
||||
status_t ARM64ArchVmAspace::Map(vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped) {
|
||||
return arch_internal::arch_mmu_map(&aspace_, vaddr, paddr, count, mmu_flags, mapped);
|
||||
}
|
||||
|
||||
status_t ARM64ArchVmAspace::Unmap(vaddr_t vaddr, size_t count, size_t* unmapped) {
|
||||
return arch_internal::arch_mmu_unmap(&aspace_, vaddr, count, unmapped);
|
||||
}
|
||||
|
||||
status_t ARM64ArchVmAspace::Protect(vaddr_t vaddr, size_t count, uint mmu_flags) {
|
||||
return arch_internal::arch_mmu_protect(&aspace_, vaddr, count, mmu_flags);
|
||||
}
|
||||
|
||||
status_t ARM64ArchVmAspace::Query(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) {
|
||||
return arch_internal::arch_mmu_query(&aspace_, vaddr, paddr, mmu_flags);
|
||||
}
|
||||
|
||||
void ARM64ArchVmAspace::ContextSwitch(ARM64ArchVmAspace *from, ARM64ArchVmAspace *to) {
|
||||
arch_internal::arch_mmu_context_switch(from ? &from->aspace_ : nullptr, to ? &to->aspace_ : nullptr);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ status_t x86_bootstrap16_prep(
|
||||
(entry64 - (uintptr_t)x86_bootstrap16_start);
|
||||
ASSERT(long_mode_entry <= UINT32_MAX);
|
||||
|
||||
uint64_t phys_bootstrap_pml4 = bootstrap_aspace->arch_aspace().pt_phys;
|
||||
uint64_t phys_bootstrap_pml4 = bootstrap_aspace->arch_aspace().GetInnerAspace().pt_phys;
|
||||
uint64_t phys_kernel_pml4 = x86_get_cr3();
|
||||
if (phys_bootstrap_pml4 > UINT32_MAX) {
|
||||
// TODO(teisenbe): Once the pmm supports it, we should request that this
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <arch/x86/descriptor.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/stats.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/fault.h>
|
||||
#include <platform.h>
|
||||
|
||||
|
||||
@@ -7,11 +7,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <magenta/compiler.h>
|
||||
#include <arch/x86/mmu.h>
|
||||
#include <kernel/spinlock.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
|
||||
#define ARCH_ASPACE_MAGIC 0x41524153 // ARAS
|
||||
|
||||
@@ -39,5 +37,51 @@ struct arch_aspace {
|
||||
spin_lock_t io_bitmap_lock;
|
||||
};
|
||||
|
||||
__END_CDECLS
|
||||
class X86ArchVmAspace final : public ArchVmAspaceBase {
|
||||
public:
|
||||
X86ArchVmAspace();
|
||||
virtual ~X86ArchVmAspace();
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(X86ArchVmAspace);
|
||||
|
||||
virtual status_t Init(vaddr_t base, size_t size, uint mmu_flags);
|
||||
virtual status_t Destroy();
|
||||
|
||||
// main methods
|
||||
virtual status_t Map(vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped);
|
||||
virtual status_t Unmap(vaddr_t vaddr, size_t count, size_t* unmapped);
|
||||
virtual status_t Protect(vaddr_t vaddr, size_t count, uint mmu_flags);
|
||||
virtual status_t Query(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags);
|
||||
|
||||
static void ContextSwitch(X86ArchVmAspace *from, X86ArchVmAspace *to);
|
||||
|
||||
arch_aspace& GetInnerAspace() { return aspace_; }
|
||||
|
||||
private:
|
||||
arch_aspace aspace_ = {};
|
||||
};
|
||||
|
||||
class X86ArchVmGuestAspace final : public ArchVmAspaceBase {
|
||||
public:
|
||||
X86ArchVmGuestAspace();
|
||||
virtual ~X86ArchVmGuestAspace();
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(X86ArchVmGuestAspace);
|
||||
|
||||
virtual status_t Init(vaddr_t base, size_t size, uint mmu_flags);
|
||||
virtual status_t Destroy();
|
||||
|
||||
// main methods
|
||||
virtual status_t Map(vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped);
|
||||
virtual status_t Unmap(vaddr_t vaddr, size_t count, size_t* unmapped);
|
||||
virtual status_t Protect(vaddr_t vaddr, size_t count, uint mmu_flags);
|
||||
virtual status_t Query(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags);
|
||||
|
||||
paddr_t Pml4Address() const { return aspace_.pt_phys; }
|
||||
|
||||
private:
|
||||
arch_aspace aspace_ = {};
|
||||
};
|
||||
|
||||
using ArchVmAspace = X86ArchVmAspace;
|
||||
using ArchVmGuestAspace = X86ArchVmGuestAspace;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <magenta/compiler.h>
|
||||
#include <dev/interrupt.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <kernel/auto_lock.h>
|
||||
#include <kernel/mp.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/vm_aspace.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
@@ -25,7 +26,7 @@
|
||||
/* Task used for updating IO permissions on each CPU */
|
||||
struct ioport_update_context {
|
||||
// aspace that we're trying to update
|
||||
arch_aspace_t* aspace;
|
||||
ArchVmAspace* aspace;
|
||||
};
|
||||
static void ioport_update_task(void* raw_context) {
|
||||
DEBUG_ASSERT(arch_ints_disabled());
|
||||
@@ -33,18 +34,18 @@ static void ioport_update_task(void* raw_context) {
|
||||
(struct ioport_update_context*)raw_context;
|
||||
|
||||
VmAspace *aspace = vmm_aspace_to_obj(get_current_thread()->aspace);
|
||||
struct arch_aspace* as = &aspace->arch_aspace();
|
||||
ArchVmAspace *as = &aspace->arch_aspace();
|
||||
if (as != context->aspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&as->io_bitmap_lock);
|
||||
spin_lock(&as->GetInnerAspace().io_bitmap_lock);
|
||||
|
||||
// This is overkill, but it's much simpler to reason about
|
||||
x86_reset_tss_io_bitmap();
|
||||
x86_set_tss_io_bitmap(*static_cast<bitmap::RleBitmap*>(as->io_bitmap));
|
||||
x86_set_tss_io_bitmap(*static_cast<bitmap::RleBitmap*>(as->GetInnerAspace().io_bitmap));
|
||||
|
||||
spin_unlock(&as->io_bitmap_lock);
|
||||
spin_unlock(&as->GetInnerAspace().io_bitmap_lock);
|
||||
}
|
||||
|
||||
int x86_set_io_bitmap(uint32_t port, uint32_t len, bool enable) {
|
||||
@@ -54,10 +55,10 @@ int x86_set_io_bitmap(uint32_t port, uint32_t len, bool enable) {
|
||||
return ERR_INVALID_ARGS;
|
||||
|
||||
VmAspace *aspace = vmm_aspace_to_obj(get_current_thread()->aspace);
|
||||
struct arch_aspace* as = &aspace->arch_aspace();
|
||||
ArchVmAspace *as = &aspace->arch_aspace();
|
||||
|
||||
mxtl::unique_ptr<bitmap::RleBitmap> optimistic_bitmap;
|
||||
if (!as->io_bitmap) {
|
||||
if (!as->GetInnerAspace().io_bitmap) {
|
||||
// Optimistically allocate a bitmap structure if we don't have one, and
|
||||
// we'll see if we actually need this allocation later. In the common
|
||||
// case, when we make the allocation we will use it.
|
||||
@@ -86,12 +87,12 @@ int x86_set_io_bitmap(uint32_t port, uint32_t len, bool enable) {
|
||||
|
||||
status_t status = NO_ERROR;
|
||||
do {
|
||||
AutoSpinLock guard(as->io_bitmap_lock);
|
||||
AutoSpinLock guard(as->GetInnerAspace().io_bitmap_lock);
|
||||
|
||||
if (!as->io_bitmap) {
|
||||
as->io_bitmap = optimistic_bitmap.release();
|
||||
if (!as->GetInnerAspace().io_bitmap) {
|
||||
as->GetInnerAspace().io_bitmap = optimistic_bitmap.release();
|
||||
}
|
||||
auto bitmap = static_cast<bitmap::RleBitmap*>(as->io_bitmap);
|
||||
auto bitmap = static_cast<bitmap::RleBitmap*>(as->GetInnerAspace().io_bitmap);
|
||||
DEBUG_ASSERT(bitmap);
|
||||
|
||||
status = enable ?
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <arch/x86/mmu_mem_types.h>
|
||||
#include <kernel/mp.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
#include <kernel/vm/pmm.h>
|
||||
|
||||
#include <bitmap/rle-bitmap.h>
|
||||
@@ -1144,7 +1145,7 @@ static status_t mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, const size_t cou
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, const size_t count,
|
||||
static status_t arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, const size_t count,
|
||||
size_t* unmapped) {
|
||||
return mmu_unmap<PageTable>(aspace, vaddr, count, unmapped);
|
||||
}
|
||||
@@ -1195,7 +1196,7 @@ static status_t mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, con
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, const size_t count,
|
||||
static status_t arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, const size_t count,
|
||||
uint mmu_flags, size_t* mapped) {
|
||||
return mmu_map<PageTable>(aspace, vaddr, paddr, count, mmu_flags, mapped);
|
||||
}
|
||||
@@ -1238,7 +1239,7 @@ static status_t mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count,
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, uint mmu_flags) {
|
||||
static status_t arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, uint mmu_flags) {
|
||||
return mmu_protect<PageTable>(aspace, vaddr, count, mmu_flags);
|
||||
}
|
||||
|
||||
@@ -1277,7 +1278,7 @@ void x86_mmu_init(void) {}
|
||||
/*
|
||||
* Fill in the high level x86 arch aspace structure and allocating a top level page table.
|
||||
*/
|
||||
status_t arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size, uint mmu_flags) {
|
||||
static status_t arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size, uint mmu_flags) {
|
||||
DEBUG_ASSERT(aspace);
|
||||
DEBUG_ASSERT(aspace->magic != ARCH_ASPACE_MAGIC);
|
||||
|
||||
@@ -1384,7 +1385,7 @@ status_t mmu_destroy_aspace(arch_aspace_t* aspace) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_destroy_aspace(arch_aspace_t* aspace) {
|
||||
static status_t arch_mmu_destroy_aspace(arch_aspace_t* aspace) {
|
||||
return mmu_destroy_aspace<PageTable>(aspace);
|
||||
}
|
||||
|
||||
@@ -1392,7 +1393,7 @@ status_t guest_mmu_destroy_paspace(guest_paspace_t* paspace) {
|
||||
return mmu_destroy_aspace<ExtendedPageTable>(paspace);
|
||||
}
|
||||
|
||||
void arch_mmu_context_switch(arch_aspace_t* old_aspace, arch_aspace_t* aspace) {
|
||||
static void arch_mmu_context_switch(arch_aspace_t* old_aspace, arch_aspace_t* aspace) {
|
||||
mp_cpu_mask_t cpu_bit = 1U << arch_curr_cpu_num();
|
||||
if (aspace != nullptr) {
|
||||
DEBUG_ASSERT(aspace->magic == ARCH_ASPACE_MAGIC);
|
||||
@@ -1481,7 +1482,7 @@ static status_t mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr,
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t arch_mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) {
|
||||
static status_t arch_mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) {
|
||||
return mmu_query<PageTable>(aspace, vaddr, paddr, mmu_flags, x86_mmu_flags);
|
||||
}
|
||||
|
||||
@@ -1510,3 +1511,39 @@ void x86_mmu_percpu_init(void) {
|
||||
efer_msr |= X86_EFER_NXE;
|
||||
write_msr(X86_MSR_IA32_EFER, efer_msr);
|
||||
}
|
||||
|
||||
// OO STUFF
|
||||
X86ArchVmAspace::X86ArchVmAspace() {
|
||||
}
|
||||
|
||||
X86ArchVmAspace::~X86ArchVmAspace() {
|
||||
// TODO: check that we've destroyed the aspace
|
||||
}
|
||||
|
||||
status_t X86ArchVmAspace::Init(vaddr_t base, size_t size, uint mmu_flags) {
|
||||
return arch_mmu_init_aspace(&aspace_, base, size, mmu_flags);
|
||||
}
|
||||
|
||||
status_t X86ArchVmAspace::Destroy() {
|
||||
return arch_mmu_destroy_aspace(&aspace_);
|
||||
}
|
||||
|
||||
status_t X86ArchVmAspace::Map(vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped) {
|
||||
return arch_mmu_map(&aspace_, vaddr, paddr, count, mmu_flags, mapped);
|
||||
}
|
||||
|
||||
status_t X86ArchVmAspace::Unmap(vaddr_t vaddr, size_t count, size_t* unmapped) {
|
||||
return arch_mmu_unmap(&aspace_, vaddr, count, unmapped);
|
||||
}
|
||||
|
||||
status_t X86ArchVmAspace::Protect(vaddr_t vaddr, size_t count, uint mmu_flags) {
|
||||
return arch_mmu_protect(&aspace_, vaddr, count, mmu_flags);
|
||||
}
|
||||
|
||||
status_t X86ArchVmAspace::Query(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) {
|
||||
return arch_mmu_query(&aspace_, vaddr, paddr, mmu_flags);
|
||||
}
|
||||
|
||||
void X86ArchVmAspace::ContextSwitch(X86ArchVmAspace *from, X86ArchVmAspace *to) {
|
||||
arch_mmu_context_switch(from ? &from->aspace_ : nullptr, to ? &to->aspace_ : nullptr);
|
||||
}
|
||||
|
||||
@@ -8,15 +8,16 @@
|
||||
#include <err.h>
|
||||
#include <arch/x86/mmu.h>
|
||||
#include <arch/mmu.h>
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
|
||||
static bool mmu_tests(void* context) {
|
||||
BEGIN_TEST;
|
||||
unittest_printf("creating large un-aligned vm region, and unmap it without mapping, make sure no leak (MG-315)\n");
|
||||
{
|
||||
arch_aspace_t aspace;
|
||||
ArchVmAspace aspace;
|
||||
vaddr_t base = 1UL << 20;
|
||||
size_t size = (1UL << 47) - base - (1UL << 20);
|
||||
status_t err = arch_mmu_init_aspace(&aspace, 1UL << 20, size, 0);
|
||||
status_t err = aspace.Init(1UL << 20, size, 0);
|
||||
EXPECT_EQ(err, NO_ERROR, "init aspace");
|
||||
|
||||
const uint arch_rw_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE;
|
||||
@@ -31,45 +32,45 @@ static bool mmu_tests(void* context) {
|
||||
// Map a single page to force the lower PDP of the target region
|
||||
// to be created
|
||||
size_t mapped;
|
||||
err = arch_mmu_map(&aspace, va - 3 * PAGE_SIZE, 0, 1, arch_rw_flags, &mapped);
|
||||
err = aspace.Map(va - 3 * PAGE_SIZE, 0, 1, arch_rw_flags, &mapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "map single page");
|
||||
EXPECT_EQ(mapped, 1u, "map single page");
|
||||
|
||||
// Map the last page of the region
|
||||
err = arch_mmu_map(&aspace, va + alloc_size - PAGE_SIZE, 0, 1, arch_rw_flags, &mapped);
|
||||
err = aspace.Map(va + alloc_size - PAGE_SIZE, 0, 1, arch_rw_flags, &mapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "map last page");
|
||||
EXPECT_EQ(mapped, 1u, "map single page");
|
||||
|
||||
paddr_t pa;
|
||||
uint flags;
|
||||
err = arch_mmu_query(&aspace, va + alloc_size - PAGE_SIZE, &pa, &flags);
|
||||
err = aspace.Query(va + alloc_size - PAGE_SIZE, &pa, &flags);
|
||||
EXPECT_EQ(err, NO_ERROR, "last entry is mapped");
|
||||
|
||||
// Attempt to unmap the target region (analogous to unmapping a demand
|
||||
// paged region that has only had its last page touched)
|
||||
size_t unmapped;
|
||||
err = arch_mmu_unmap(&aspace, va, alloc_size / PAGE_SIZE, &unmapped);
|
||||
err = aspace.Unmap(va, alloc_size / PAGE_SIZE, &unmapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "unmap unallocated region");
|
||||
EXPECT_EQ(unmapped, alloc_size / PAGE_SIZE, "unmap unallocated region");
|
||||
|
||||
err = arch_mmu_query(&aspace, va + alloc_size - PAGE_SIZE, &pa, &flags);
|
||||
err = aspace.Query(va + alloc_size - PAGE_SIZE, &pa, &flags);
|
||||
EXPECT_EQ(err, ERR_NOT_FOUND, "last entry is not mapped anymore");
|
||||
|
||||
// Unmap the single page from earlier
|
||||
err = arch_mmu_unmap(&aspace, va - 3 * PAGE_SIZE, 1, &unmapped);
|
||||
err = aspace.Unmap(va - 3 * PAGE_SIZE, 1, &unmapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "unmap single page");
|
||||
EXPECT_EQ(unmapped, 1u, "unmap unallocated region");
|
||||
|
||||
err = arch_mmu_destroy_aspace(&aspace);
|
||||
err = aspace.Destroy();
|
||||
EXPECT_EQ(err, NO_ERROR, "destroy aspace");
|
||||
}
|
||||
|
||||
unittest_printf("creating large un-aligned vm region, and unmap it without mapping (MG-315)\n");
|
||||
{
|
||||
arch_aspace_t aspace;
|
||||
ArchVmAspace aspace;
|
||||
vaddr_t base = 1UL << 20;
|
||||
size_t size = (1UL << 47) - base - (1UL << 20);
|
||||
status_t err = arch_mmu_init_aspace(&aspace, 1UL << 20, size, 0);
|
||||
status_t err = aspace.Init(1UL << 20, size, 0);
|
||||
EXPECT_EQ(err, NO_ERROR, "init aspace");
|
||||
|
||||
const uint arch_rw_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE;
|
||||
@@ -84,23 +85,23 @@ static bool mmu_tests(void* context) {
|
||||
// Map a single page to force the lower PDP of the target region
|
||||
// to be created
|
||||
size_t mapped;
|
||||
err = arch_mmu_map(&aspace, va - 2 * PAGE_SIZE, 0, 1, arch_rw_flags, &mapped);
|
||||
err = aspace.Map(va - 2 * PAGE_SIZE, 0, 1, arch_rw_flags, &mapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "map single page");
|
||||
EXPECT_EQ(mapped, 1u, "map single page");
|
||||
|
||||
// Attempt to unmap the target region (analogous to unmapping a demand
|
||||
// paged region that has not been touched)
|
||||
size_t unmapped;
|
||||
err = arch_mmu_unmap(&aspace, va, alloc_size / PAGE_SIZE, &unmapped);
|
||||
err = aspace.Unmap(va, alloc_size / PAGE_SIZE, &unmapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "unmap unallocated region");
|
||||
EXPECT_EQ(unmapped, alloc_size / PAGE_SIZE, "unmap unallocated region");
|
||||
|
||||
// Unmap the single page from earlier
|
||||
err = arch_mmu_unmap(&aspace, va - 2 * PAGE_SIZE, 1, &unmapped);
|
||||
err = aspace.Unmap(va - 2 * PAGE_SIZE, 1, &unmapped);
|
||||
EXPECT_EQ(err, NO_ERROR, "unmap single page");
|
||||
EXPECT_EQ(unmapped, 1u, "unmap single page");
|
||||
|
||||
err = arch_mmu_destroy_aspace(&aspace);
|
||||
err = aspace.Destroy();
|
||||
EXPECT_EQ(err, NO_ERROR, "destroy aspace");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <arch/x86/feature.h>
|
||||
#include <arch/x86/proc_trace.h>
|
||||
#include <arch/x86/registers.h>
|
||||
#include <inttypes.h>
|
||||
#include <magenta/compiler.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include <kernel/thread.h>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include <arch/mmu.h>
|
||||
#include <assert.h>
|
||||
#include <magenta/compiler.h>
|
||||
#include <debug.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
#include <kernel/vm/vm_object.h>
|
||||
#include <kernel/vm/vm_object_physical.h>
|
||||
#include <list.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define ARCH_MMU_FLAG_GUEST_PASPACE (1u << 8) /* guest physical address space */
|
||||
|
||||
/* initialize per address space */
|
||||
namespace dontuse {
|
||||
status_t guest_mmu_init_paspace(guest_paspace_t* paspace, size_t size) __NONNULL((1));
|
||||
status_t guest_mmu_destroy_paspace(guest_paspace_t* paspace) __NONNULL((1));
|
||||
|
||||
@@ -19,3 +20,4 @@ status_t guest_mmu_map(guest_paspace_t* paspace, vaddr_t vaddr, paddr_t paddr, s
|
||||
status_t guest_mmu_unmap(guest_paspace_t* paspace, vaddr_t vaddr, size_t count, size_t* unmapped) __NONNULL((1));
|
||||
status_t guest_mmu_protect(guest_paspace_t* paspace, vaddr_t vaddr, size_t count, uint mmu_flags) __NONNULL((1));
|
||||
status_t guest_mmu_query(guest_paspace_t* paspace, vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) __NONNULL((1));
|
||||
};
|
||||
|
||||
+16
-29
@@ -11,45 +11,32 @@
|
||||
#include <magenta/compiler.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* to bring in definition of arch_aspace */
|
||||
#include <arch/aspace.h>
|
||||
|
||||
#define ARCH_MMU_FLAG_CACHED (0 << 0)
|
||||
#define ARCH_MMU_FLAG_UNCACHED (1 << 0)
|
||||
#define ARCH_MMU_FLAG_UNCACHED_DEVICE (2 << 0) /* only exists on some arches, otherwise UNCACHED */
|
||||
#define ARCH_MMU_FLAG_WRITE_COMBINING (3 << 0) /* only exists on some arches, otherwise UNCACHED */
|
||||
#define ARCH_MMU_FLAG_CACHE_MASK (3 << 0)
|
||||
|
||||
#define ARCH_MMU_FLAG_PERM_USER (1 << 2)
|
||||
#define ARCH_MMU_FLAG_PERM_READ (1 << 3)
|
||||
#define ARCH_MMU_FLAG_PERM_WRITE (1 << 4)
|
||||
#define ARCH_MMU_FLAG_PERM_EXECUTE (1 << 5)
|
||||
#define ARCH_MMU_FLAG_NS (1 << 6) /* NON-SECURE */
|
||||
#define ARCH_MMU_FLAG_INVALID (1 << 7) /* indicates that flags are not specified */
|
||||
|
||||
/* forward declare the per-address space arch-specific context object */
|
||||
// forward declare the per-address space arch-specific context object
|
||||
typedef struct arch_aspace arch_aspace_t;
|
||||
|
||||
#define ARCH_ASPACE_FLAG_KERNEL (1 << 0)
|
||||
#if 0
|
||||
// put all of these inside an internal namespace to discourage older code from calling directly
|
||||
namespace arch_internal {
|
||||
|
||||
/* initialize per address space */
|
||||
status_t arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size, uint mmu_flags) __NONNULL((1));
|
||||
status_t arch_mmu_destroy_aspace(arch_aspace_t* aspace) __NONNULL((1));
|
||||
// initialize per address space
|
||||
status_t arch_mmu_init_aspace(arch_aspace_t* aspace, vaddr_t base, size_t size, uint mmu_flags);
|
||||
status_t arch_mmu_destroy_aspace(arch_aspace_t* aspace);
|
||||
|
||||
/* routines to map/unmap/update permissions/query mappings per address space */
|
||||
status_t arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped) __NONNULL((1));
|
||||
status_t arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, size_t* unmapped) __NONNULL((1));
|
||||
status_t arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, uint mmu_flags) __NONNULL((1));
|
||||
status_t arch_mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) __NONNULL((1));
|
||||
// routines to map/unmap/update permissions/query mappings per address space
|
||||
status_t arch_mmu_map(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped);
|
||||
status_t arch_mmu_unmap(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, size_t* unmapped);
|
||||
status_t arch_mmu_protect(arch_aspace_t* aspace, vaddr_t vaddr, size_t count, uint mmu_flags);
|
||||
status_t arch_mmu_query(arch_aspace_t* aspace, vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags);
|
||||
|
||||
vaddr_t arch_mmu_pick_spot(const arch_aspace_t* aspace,
|
||||
vaddr_t base, uint prev_region_mmu_flags,
|
||||
vaddr_t end, uint next_region_mmu_flags,
|
||||
vaddr_t align, size_t size, uint mmu_flags) __NONNULL((1));
|
||||
|
||||
vaddr_t align, size_t size, uint mmu_flags);
|
||||
/* load a new user address space context.
|
||||
* aspace argument NULL should unload user space.
|
||||
*/
|
||||
void arch_mmu_context_switch(arch_aspace_t* old_aspace, arch_aspace_t* aspace);
|
||||
|
||||
void arch_disable_mmu(void);
|
||||
} // namespace arch_internal
|
||||
#endif
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <arch/thread.h>
|
||||
#include <kernel/wait.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <debug.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
@@ -68,6 +67,8 @@ typedef void (*thread_user_callback_t)(enum thread_user_state_change new_state,
|
||||
|
||||
#define THREAD_LINEBUFFER_LENGTH 128
|
||||
|
||||
struct vmm_aspace;
|
||||
|
||||
typedef struct thread {
|
||||
int magic;
|
||||
struct list_node thread_list_node;
|
||||
@@ -87,7 +88,7 @@ typedef struct thread {
|
||||
int pinned_cpu; /* only run on pinned_cpu if >= 0 */
|
||||
|
||||
/* pointer to the kernel address space this thread is associated with */
|
||||
vmm_aspace_t *aspace;
|
||||
struct vmm_aspace *aspace;
|
||||
|
||||
/* pointer to user thread if one exists for this thread */
|
||||
void *user_thread;
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <arch.h>
|
||||
#include <arch/mmu.h>
|
||||
#include <assert.h>
|
||||
#include <kernel/vm/page.h>
|
||||
#include <list.h>
|
||||
#include <magenta/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2017 The Fuchsia Authors
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mxtl/canary.h>
|
||||
#include <mxtl/macros.h>
|
||||
#include <sys/types.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
// flags
|
||||
const uint ARCH_MMU_FLAG_CACHED = (0u << 0);
|
||||
const uint ARCH_MMU_FLAG_UNCACHED = (1u << 0);
|
||||
const uint ARCH_MMU_FLAG_UNCACHED_DEVICE = (2u << 0); // only exists on some arches, otherwise UNCACHED
|
||||
const uint ARCH_MMU_FLAG_WRITE_COMBINING = (3u << 0); // only exists on some arches, otherwise UNCACHED
|
||||
const uint ARCH_MMU_FLAG_CACHE_MASK = (3u << 0);
|
||||
const uint ARCH_MMU_FLAG_PERM_USER = (1u << 2);
|
||||
const uint ARCH_MMU_FLAG_PERM_READ = (1u << 3);
|
||||
const uint ARCH_MMU_FLAG_PERM_WRITE = (1u << 4);
|
||||
const uint ARCH_MMU_FLAG_PERM_EXECUTE = (1u << 5);
|
||||
const uint ARCH_MMU_FLAG_NS = (1u << 6); // NON-SECURE
|
||||
const uint ARCH_MMU_FLAG_INVALID = (1u << 7); // indicates that flags are not specified
|
||||
|
||||
const uint ARCH_ASPACE_FLAG_KERNEL = (1u << 0);
|
||||
|
||||
// per arch base class api to encapsulate the mmu routines on an aspace
|
||||
class ArchVmAspaceBase {
|
||||
public:
|
||||
ArchVmAspaceBase() {}
|
||||
virtual ~ArchVmAspaceBase() {}
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(ArchVmAspaceBase);
|
||||
|
||||
virtual status_t Init(vaddr_t base, size_t size, uint mmu_flags) = 0;
|
||||
virtual status_t Destroy() = 0;
|
||||
|
||||
// main methods
|
||||
virtual status_t Map(vaddr_t vaddr, paddr_t paddr, size_t count, uint mmu_flags, size_t* mapped) = 0;
|
||||
virtual status_t Unmap(vaddr_t vaddr, size_t count, size_t* unmapped) = 0;
|
||||
virtual status_t Protect(vaddr_t vaddr, size_t count, uint mmu_flags) = 0;
|
||||
virtual status_t Query(vaddr_t vaddr, paddr_t* paddr, uint* mmu_flags) = 0;
|
||||
|
||||
virtual vaddr_t PickSpot(vaddr_t base, uint prev_region_mmu_flags,
|
||||
vaddr_t end, uint next_region_mmu_flags,
|
||||
vaddr_t align, size_t size, uint mmu_flags) {
|
||||
return PAGE_ALIGN(base);
|
||||
}
|
||||
|
||||
static void ContextSwitch(ArchVmAspaceBase *from, ArchVmAspaceBase *to);
|
||||
|
||||
private:
|
||||
mxtl::Canary<mxtl::magic("VAAS")> canary_;
|
||||
};
|
||||
|
||||
#include <arch/aspace.h>
|
||||
|
||||
@@ -11,11 +11,8 @@
|
||||
#include <magenta/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if __cplusplus
|
||||
// forward declare
|
||||
class VmObject;
|
||||
#endif
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
// core per page structure
|
||||
typedef struct vm_page {
|
||||
@@ -30,13 +27,11 @@ typedef struct vm_page {
|
||||
// in allocated/just freed state, use a linked list to hold the page in a queue
|
||||
struct list_node node;
|
||||
} free;
|
||||
#if __cplusplus
|
||||
struct {
|
||||
// attached to a vm object
|
||||
uint64_t offset;
|
||||
VmObject* obj;
|
||||
} object;
|
||||
#endif
|
||||
|
||||
uint8_t pad[24]; // pad out to 32 bytes
|
||||
};
|
||||
@@ -64,5 +59,3 @@ static inline bool page_is_free(const vm_page_t* page) {
|
||||
|
||||
const char* page_state_to_string(unsigned int state);
|
||||
void dump_page(const vm_page_t* page);
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
#include <magenta/compiler.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
// physical allocator
|
||||
typedef struct pmm_arena_info {
|
||||
char name[16];
|
||||
@@ -90,5 +88,3 @@ paddr_t vm_page_to_paddr(const vm_page_t* page);
|
||||
|
||||
// paddr to vm_page_t
|
||||
vm_page_t* paddr_to_vm_page(paddr_t addr);
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/vm_address_region.h>
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
#include <lib/crypto/prng.h>
|
||||
#include <mxtl/canary.h>
|
||||
#include <mxtl/intrusive_double_list.h>
|
||||
@@ -45,7 +46,7 @@ public:
|
||||
vaddr_t base() const { return base_; }
|
||||
size_t size() const { return size_; }
|
||||
const char* name() const { return name_; }
|
||||
arch_aspace_t& arch_aspace() { return arch_aspace_; }
|
||||
ArchVmAspace& arch_aspace() { return arch_aspace_; }
|
||||
bool is_user() const { return (flags_ & TYPE_MASK) == TYPE_USER; }
|
||||
bool is_aslr_enabled() const { return aslr_enabled_; }
|
||||
|
||||
@@ -194,7 +195,7 @@ private:
|
||||
uint8_t aslr_seed_[crypto::PRNG::kMinEntropy];
|
||||
|
||||
// architecturally specific part of the aspace
|
||||
arch_aspace_t arch_aspace_ = {};
|
||||
ArchVmAspace arch_aspace_;
|
||||
|
||||
#if WITH_LIB_VDSO
|
||||
mxtl::RefPtr<VmMapping> vdso_code_mapping_;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <assert.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <kernel/vm/page.h>
|
||||
#include <kernel/vm/vm_page_list.h>
|
||||
#include <lib/user_copy/user_ptr.h>
|
||||
#include <list.h>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2017 The Fuchsia Authors
|
||||
//
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
|
||||
#include <kernel/vm.h>
|
||||
#include <magenta/errors.h>
|
||||
|
||||
@@ -16,6 +16,7 @@ MODULE_DEPS += \
|
||||
third_party/lib/cryptolib
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/arch_vm_aspace.cpp \
|
||||
$(LOCAL_DIR)/bootalloc.cpp \
|
||||
$(LOCAL_DIR)/page.cpp \
|
||||
$(LOCAL_DIR)/pmm.cpp \
|
||||
|
||||
@@ -60,8 +60,7 @@ void MarkPagesInUse(vaddr_t va, size_t len) {
|
||||
uint flags;
|
||||
paddr_t pa;
|
||||
|
||||
status_t err = arch_mmu_query(&VmAspace::kernel_aspace()->arch_aspace(), va + offset,
|
||||
&pa, &flags);
|
||||
status_t err = VmAspace::kernel_aspace()->arch_aspace().Query(va + offset, &pa, &flags);
|
||||
if (err >= 0) {
|
||||
LTRACEF("va %#" PRIxPTR ", pa %#" PRIxPTR ", flags %#x, err %d, start_pa %#" PRIxPTR
|
||||
" runlen %#" PRIxPTR "\n",
|
||||
@@ -261,7 +260,7 @@ paddr_t vaddr_to_paddr(const void* ptr) {
|
||||
return (paddr_t) nullptr;
|
||||
|
||||
paddr_t pa;
|
||||
status_t rc = arch_mmu_query(&aspace->arch_aspace(), (vaddr_t)ptr, &pa, nullptr);
|
||||
status_t rc = aspace->arch_aspace().Query((vaddr_t)ptr, &pa, nullptr);
|
||||
if (rc)
|
||||
return (paddr_t) nullptr;
|
||||
|
||||
@@ -299,7 +298,7 @@ static int cmd_vm(int argc, const cmd_args* argv, uint32_t flags) {
|
||||
|
||||
paddr_t pa;
|
||||
uint flags;
|
||||
status_t err = arch_mmu_query(&aspace->arch_aspace(), argv[2].u, &pa, &flags);
|
||||
status_t err = aspace->arch_aspace().Query(argv[2].u, &pa, &flags);
|
||||
printf("arch_mmu_query returns %d\n", err);
|
||||
if (err >= 0) {
|
||||
printf("\tpa %#" PRIxPTR ", flags %#x\n", pa, flags);
|
||||
@@ -316,7 +315,7 @@ static int cmd_vm(int argc, const cmd_args* argv, uint32_t flags) {
|
||||
|
||||
size_t mapped;
|
||||
auto err =
|
||||
arch_mmu_map(&aspace->arch_aspace(), argv[3].u, argv[2].u, (uint)argv[4].u, (uint)argv[5].u, &mapped);
|
||||
aspace->arch_aspace().Map(argv[3].u, argv[2].u, (uint)argv[4].u, (uint)argv[5].u, &mapped);
|
||||
printf("arch_mmu_map returns %d, mapped %zu\n", err, mapped);
|
||||
} else if (!strcmp(argv[1].str, "unmap")) {
|
||||
if (argc < 4)
|
||||
@@ -329,7 +328,7 @@ static int cmd_vm(int argc, const cmd_args* argv, uint32_t flags) {
|
||||
}
|
||||
|
||||
size_t unmapped;
|
||||
auto err = arch_mmu_unmap(&aspace->arch_aspace(), argv[2].u, (uint)argv[3].u, &unmapped);
|
||||
auto err = aspace->arch_aspace().Unmap(argv[2].u, (uint)argv[3].u, &unmapped);
|
||||
printf("arch_mmu_unmap returns %d, unmapped %zu\n", err, unmapped);
|
||||
} else {
|
||||
printf("unknown command\n");
|
||||
|
||||
@@ -486,8 +486,7 @@ bool VmAddressRegion::CheckGapLocked(const ChildList::iterator& prev,
|
||||
? next->as_vm_mapping()->arch_mmu_flags()
|
||||
: ARCH_MMU_FLAG_INVALID;
|
||||
|
||||
*pva =
|
||||
arch_mmu_pick_spot(&aspace_->arch_aspace(), real_gap_beg, prev_arch_mmu_flags, real_gap_end,
|
||||
*pva = aspace_->arch_aspace().PickSpot(real_gap_beg, prev_arch_mmu_flags, real_gap_end,
|
||||
next_arch_mmu_flags, align, region_size, arch_mmu_flags);
|
||||
if (*pva < real_gap_beg)
|
||||
goto not_found; // address wrapped around
|
||||
|
||||
@@ -139,7 +139,8 @@ status_t VmAspace::Init() {
|
||||
// intialize the architectually specific part
|
||||
bool is_high_kernel = (flags_ & TYPE_MASK) == TYPE_KERNEL;
|
||||
uint arch_aspace_flags = is_high_kernel ? ARCH_ASPACE_FLAG_KERNEL : 0;
|
||||
status_t status = arch_mmu_init_aspace(&arch_aspace_, base_, size_, arch_aspace_flags);
|
||||
|
||||
status_t status = arch_aspace_.Init(base_, size_, arch_aspace_flags);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
@@ -217,7 +218,7 @@ VmAspace::~VmAspace() {
|
||||
// TODO(teisenbe): Move this to Destroy(). Currently can't move since
|
||||
// ProcessDispatcher calls Destroy() from the context of a thread in the
|
||||
// aspace.
|
||||
arch_mmu_destroy_aspace(&arch_aspace_);
|
||||
arch_aspace_.Destroy();
|
||||
}
|
||||
|
||||
mxtl::RefPtr<VmAddressRegion> VmAspace::RootVmar() {
|
||||
@@ -363,7 +364,7 @@ status_t VmAspace::ReserveSpace(const char* name, size_t size, vaddr_t vaddr) {
|
||||
|
||||
// lookup how it's already mapped
|
||||
uint arch_mmu_flags = 0;
|
||||
auto err = arch_mmu_query(&arch_aspace_, vaddr, nullptr, &arch_mmu_flags);
|
||||
auto err = arch_aspace_.Query(vaddr, nullptr, &arch_mmu_flags);
|
||||
if (err) {
|
||||
// if it wasn't already mapped, use some sort of strict default
|
||||
arch_mmu_flags = ARCH_MMU_FLAG_CACHED | ARCH_MMU_FLAG_PERM_READ;
|
||||
|
||||
@@ -120,8 +120,7 @@ status_t VmMapping::ProtectLocked(vaddr_t base, size_t size, uint new_arch_mmu_f
|
||||
|
||||
// If we're changing the whole mapping, just make the change.
|
||||
if (base_ == base && size_ == size) {
|
||||
status_t status = arch_mmu_protect(&aspace_->arch_aspace(), base, size / PAGE_SIZE,
|
||||
new_arch_mmu_flags);
|
||||
status_t status = aspace_->arch_aspace().Protect(base, size / PAGE_SIZE, new_arch_mmu_flags);
|
||||
LTRACEF("arch_mmu_protect returns %d\n", status);
|
||||
arch_mmu_flags_ = new_arch_mmu_flags;
|
||||
return NO_ERROR;
|
||||
@@ -138,7 +137,7 @@ status_t VmMapping::ProtectLocked(vaddr_t base, size_t size, uint new_arch_mmu_f
|
||||
return ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t status = arch_mmu_protect(&aspace_->arch_aspace(), base, size / PAGE_SIZE,
|
||||
status_t status = aspace_->arch_aspace().Protect(base, size / PAGE_SIZE,
|
||||
new_arch_mmu_flags);
|
||||
LTRACEF("arch_mmu_protect returns %d\n", status);
|
||||
arch_mmu_flags_ = new_arch_mmu_flags;
|
||||
@@ -161,7 +160,7 @@ status_t VmMapping::ProtectLocked(vaddr_t base, size_t size, uint new_arch_mmu_f
|
||||
return ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t status = arch_mmu_protect(&aspace_->arch_aspace(), base, size / PAGE_SIZE,
|
||||
status_t status = aspace_->arch_aspace().Protect(base, size / PAGE_SIZE,
|
||||
new_arch_mmu_flags);
|
||||
LTRACEF("arch_mmu_protect returns %d\n", status);
|
||||
|
||||
@@ -190,7 +189,7 @@ status_t VmMapping::ProtectLocked(vaddr_t base, size_t size, uint new_arch_mmu_f
|
||||
return ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
status_t status = arch_mmu_protect(&aspace_->arch_aspace(), base, size / PAGE_SIZE,
|
||||
status_t status = aspace_->arch_aspace().Protect(base, size / PAGE_SIZE,
|
||||
new_arch_mmu_flags);
|
||||
LTRACEF("arch_mmu_protect returns %d\n", status);
|
||||
|
||||
@@ -257,7 +256,7 @@ status_t VmMapping::UnmapLocked(vaddr_t base, size_t size) {
|
||||
// Check if unmapping from one of the ends
|
||||
if (base_ == base || base + size == base_ + size_) {
|
||||
LTRACEF("unmapping base %#lx size %#zx\n", base, size);
|
||||
status_t status = arch_mmu_unmap(&aspace_->arch_aspace(), base, size / PAGE_SIZE, nullptr);
|
||||
status_t status = aspace_->arch_aspace().Unmap(base, size / PAGE_SIZE, nullptr);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
@@ -292,7 +291,7 @@ status_t VmMapping::UnmapLocked(vaddr_t base, size_t size) {
|
||||
|
||||
// Unmap the middle segment
|
||||
LTRACEF("unmapping base %#lx size %#zx\n", base, size);
|
||||
status_t status = arch_mmu_unmap(&aspace_->arch_aspace(), base, size / PAGE_SIZE, nullptr);
|
||||
status_t status = aspace_->arch_aspace().Unmap(base, size / PAGE_SIZE, nullptr);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
@@ -362,7 +361,7 @@ status_t VmMapping::UnmapVmoRangeLocked(uint64_t offset, uint64_t len) const {
|
||||
LTRACEF("going to unmap %#" PRIxPTR ", len %#" PRIx64 " aspace %p\n",
|
||||
unmap_base.ValueOrDie(), len_new, aspace_.get());
|
||||
|
||||
status_t status = arch_mmu_unmap(&aspace_->arch_aspace(), unmap_base.ValueOrDie(),
|
||||
status_t status = aspace_->arch_aspace().Unmap(unmap_base.ValueOrDie(),
|
||||
static_cast<size_t>(len_new) / PAGE_SIZE, nullptr);
|
||||
if (status < 0)
|
||||
return status;
|
||||
@@ -422,7 +421,7 @@ status_t VmMapping::MapRange(size_t offset, size_t len, bool commit) {
|
||||
LTRACEF_LEVEL(2, "mapping pa %#" PRIxPTR " to va %#" PRIxPTR "\n", pa, va);
|
||||
|
||||
size_t mapped;
|
||||
auto ret = arch_mmu_map(&aspace_->arch_aspace(), va, pa, 1, arch_mmu_flags_, &mapped);
|
||||
auto ret = aspace_->arch_aspace().Map(va, pa, 1, arch_mmu_flags_, &mapped);
|
||||
if (ret < 0) {
|
||||
TRACEF("error %d mapping page at va %#" PRIxPTR " pa %#" PRIxPTR "\n", ret, va, pa);
|
||||
}
|
||||
@@ -566,7 +565,7 @@ status_t VmMapping::PageFault(vaddr_t va, const uint pf_flags) {
|
||||
// this may happen if we are one of multiple threads racing on a single address
|
||||
uint page_flags;
|
||||
paddr_t pa;
|
||||
status_t err = arch_mmu_query(&aspace_->arch_aspace(), va, &pa, &page_flags);
|
||||
status_t err = aspace_->arch_aspace().Query(va, &pa, &page_flags);
|
||||
if (err >= 0) {
|
||||
LTRACEF("queried va, page at pa %#" PRIxPTR ", flags %#x is already there\n", pa,
|
||||
page_flags);
|
||||
@@ -581,7 +580,7 @@ status_t VmMapping::PageFault(vaddr_t va, const uint pf_flags) {
|
||||
DEBUG_ASSERT((pa != vm_get_zero_page_paddr()) || !(mmu_flags & ARCH_MMU_FLAG_PERM_WRITE));
|
||||
|
||||
// same page, different permission
|
||||
status = arch_mmu_protect(&aspace_->arch_aspace(), va, 1, mmu_flags);
|
||||
status = aspace_->arch_aspace().Protect(va, 1, mmu_flags);
|
||||
if (status < 0) {
|
||||
TRACEF("failed to modify permissions on existing mapping\n");
|
||||
return ERR_NO_MEMORY;
|
||||
@@ -596,14 +595,14 @@ status_t VmMapping::PageFault(vaddr_t va, const uint pf_flags) {
|
||||
DEBUG_ASSERT((new_pa != vm_get_zero_page_paddr()) || !(mmu_flags & ARCH_MMU_FLAG_PERM_WRITE));
|
||||
|
||||
// unmap the old one and put the new one in place
|
||||
status = arch_mmu_unmap(&aspace_->arch_aspace(), va, 1, nullptr);
|
||||
status = aspace_->arch_aspace().Unmap(va, 1, nullptr);
|
||||
if (status < 0) {
|
||||
TRACEF("failed to remove old mapping before replacing\n");
|
||||
return ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
size_t mapped;
|
||||
status = arch_mmu_map(&aspace_->arch_aspace(), va, new_pa, 1, mmu_flags, &mapped);
|
||||
status = aspace_->arch_aspace().Map(va, new_pa, 1, mmu_flags, &mapped);
|
||||
if (status < 0) {
|
||||
TRACEF("failed to map replacement page\n");
|
||||
return ERR_NO_MEMORY;
|
||||
@@ -621,7 +620,7 @@ status_t VmMapping::PageFault(vaddr_t va, const uint pf_flags) {
|
||||
DEBUG_ASSERT((new_pa != vm_get_zero_page_paddr()) || !(mmu_flags & ARCH_MMU_FLAG_PERM_WRITE));
|
||||
|
||||
size_t mapped;
|
||||
status = arch_mmu_map(&aspace_->arch_aspace(), va, new_pa, 1, mmu_flags, &mapped);
|
||||
status = aspace_->arch_aspace().Map(va, new_pa, 1, mmu_flags, &mapped);
|
||||
if (status < 0) {
|
||||
TRACEF("failed to map page\n");
|
||||
return ERR_NO_MEMORY;
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
// This file mostly contains C wrappers around the underlying C++ objects, conforming to
|
||||
// the older api.
|
||||
|
||||
static void vmm_context_switch(VmAspace* oldspace, VmAspace* newaspace);
|
||||
|
||||
static inline void vmm_context_switch(VmAspace* oldspace, VmAspace* newaspace) {
|
||||
DEBUG_ASSERT(thread_lock_held());
|
||||
|
||||
arch_mmu_context_switch(oldspace ? &oldspace->arch_aspace() : nullptr,
|
||||
newaspace ? &newaspace->arch_aspace() : nullptr);
|
||||
ArchVmAspace::ContextSwitch(oldspace ? &oldspace->arch_aspace() : nullptr,
|
||||
newaspace ? &newaspace->arch_aspace() : nullptr);
|
||||
}
|
||||
|
||||
void vmm_context_switch(vmm_aspace_t* oldspace, vmm_aspace_t* newaspace) {
|
||||
@@ -95,11 +93,6 @@ vmm_aspace_t* vmm_get_kernel_aspace(void) {
|
||||
return reinterpret_cast<vmm_aspace_t*>(VmAspace::kernel_aspace());
|
||||
}
|
||||
|
||||
arch_aspace_t* vmm_get_arch_aspace(vmm_aspace_t* aspace) {
|
||||
auto real_aspace = reinterpret_cast<VmAspace*>(aspace);
|
||||
return &real_aspace->arch_aspace();
|
||||
}
|
||||
|
||||
static int cmd_vmm(int argc, const cmd_args* argv, uint32_t flags) {
|
||||
if (argc < 2) {
|
||||
notenoughargs:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/cmpctmalloc.h>
|
||||
#include <lib/heap.h>
|
||||
#include <platform.h>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <hypervisor/guest_physical_address_space.h>
|
||||
|
||||
#include <arch/mmu.h>
|
||||
#include <kernel/vm/arch_vm_aspace.h>
|
||||
#include <kernel/vm/fault.h>
|
||||
#include <mxalloc/new.h>
|
||||
|
||||
@@ -23,7 +25,7 @@ status_t GuestPhysicalAddressSpace::Create(mxtl::RefPtr<VmObject> guest_phys_mem
|
||||
if (!ac.check())
|
||||
return ERR_NO_MEMORY;
|
||||
|
||||
status_t status = guest_mmu_init_paspace(&gpas->paspace_, kAddressSpaceSize);
|
||||
status_t status = gpas->Init(kAddressSpaceSize);
|
||||
if (status != NO_ERROR)
|
||||
return status;
|
||||
|
||||
@@ -40,41 +42,44 @@ GuestPhysicalAddressSpace::GuestPhysicalAddressSpace(mxtl::RefPtr<VmObject> gues
|
||||
: guest_phys_mem_(guest_phys_mem) {}
|
||||
|
||||
GuestPhysicalAddressSpace::~GuestPhysicalAddressSpace() {
|
||||
__UNUSED status_t status = guest_mmu_destroy_paspace(&paspace_);
|
||||
status_t status = aspace_.Destroy();
|
||||
DEBUG_ASSERT(status == NO_ERROR);
|
||||
}
|
||||
|
||||
static status_t map_page(guest_paspace_t* paspace, vaddr_t guest_paddr, paddr_t host_paddr,
|
||||
status_t GuestPhysicalAddressSpace::Init(size_t size) {
|
||||
return aspace_.Init(0, size, 0);
|
||||
}
|
||||
|
||||
static status_t map_page(ArchVmGuestAspace *aspace, vaddr_t guest_paddr, paddr_t host_paddr,
|
||||
uint mmu_flags) {
|
||||
size_t mapped;
|
||||
status_t status = guest_mmu_map(paspace, guest_paddr, host_paddr, 1, mmu_flags, &mapped);
|
||||
status_t status = aspace->Map(guest_paddr, host_paddr, 1, mmu_flags, &mapped);
|
||||
if (status != NO_ERROR)
|
||||
return status;
|
||||
return mapped != 1 ? ERR_NO_MEMORY : NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GuestPhysicalAddressSpace::MapApicPage(vaddr_t guest_paddr, paddr_t host_paddr) {
|
||||
return map_page(&paspace_, guest_paddr, host_paddr, kApicMmuFlags);
|
||||
return map_page(&aspace_, guest_paddr, host_paddr, kApicMmuFlags);
|
||||
}
|
||||
|
||||
status_t GuestPhysicalAddressSpace::MapRange(vaddr_t guest_paddr, size_t size) {
|
||||
auto mmu_map = [](void* context, size_t offset, size_t index, paddr_t pa) -> status_t {
|
||||
guest_paspace_t* paspace = static_cast<guest_paspace_t*>(context);
|
||||
return map_page(paspace, offset, pa, kMmuFlags);
|
||||
ArchVmGuestAspace* aspace = static_cast<ArchVmGuestAspace*>(context);
|
||||
return map_page(aspace, offset, pa, kMmuFlags);
|
||||
};
|
||||
return guest_phys_mem_->Lookup(guest_paddr, size, kPfFlags, mmu_map, &paspace_);
|
||||
return guest_phys_mem_->Lookup(guest_paddr, size, kPfFlags, mmu_map, &aspace_);
|
||||
}
|
||||
|
||||
status_t GuestPhysicalAddressSpace::UnmapRange(vaddr_t guest_paddr, size_t size) {
|
||||
size_t num_pages = size / PAGE_SIZE;
|
||||
size_t unmapped;
|
||||
status_t status = guest_mmu_unmap(&paspace_, guest_paddr, num_pages, &unmapped);
|
||||
status_t status = aspace_.Unmap(guest_paddr, num_pages, &unmapped);
|
||||
if (status != NO_ERROR)
|
||||
return status;
|
||||
return unmapped != num_pages ? ERR_BAD_STATE : NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GuestPhysicalAddressSpace::GetPage(vaddr_t guest_paddr, paddr_t* host_paddr) {
|
||||
uint mmu_flags;
|
||||
return guest_mmu_query(&paspace_, guest_paddr, host_paddr, &mmu_flags);
|
||||
return aspace_.Query(guest_paddr, host_paddr, nullptr);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <arch/guest_mmu.h>
|
||||
#include <arch/aspace.h>
|
||||
#include <kernel/vm/vm_object.h>
|
||||
#include <mxtl/macros.h>
|
||||
#include <mxtl/unique_ptr.h>
|
||||
|
||||
class GuestPhysicalAddressSpace {
|
||||
@@ -17,19 +19,24 @@ public:
|
||||
|
||||
~GuestPhysicalAddressSpace();
|
||||
|
||||
status_t Init(size_t size);
|
||||
|
||||
size_t size() const { return guest_phys_mem_->size(); }
|
||||
#if ARCH_X86_64
|
||||
paddr_t Pml4Address() { return paspace_.pt_phys; }
|
||||
status_t MapApicPage(vaddr_t guest_paddr, paddr_t host_paddr);
|
||||
#endif
|
||||
|
||||
status_t UnmapRange(vaddr_t guest_paddr, size_t size);
|
||||
status_t GetPage(vaddr_t guest_paddr, paddr_t* host_paddr);
|
||||
|
||||
#if ARCH_X86_64
|
||||
paddr_t Pml4Address() { return aspace_.Pml4Address(); }
|
||||
status_t MapApicPage(vaddr_t guest_paddr, paddr_t host_paddr);
|
||||
#endif
|
||||
|
||||
private:
|
||||
guest_paspace_t paspace_;
|
||||
ArchVmGuestAspace aspace_;
|
||||
mxtl::RefPtr<VmObject> guest_phys_mem_;
|
||||
|
||||
explicit GuestPhysicalAddressSpace(mxtl::RefPtr<VmObject> guest_phys_mem);
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(GuestPhysicalAddressSpace);
|
||||
|
||||
status_t MapRange(vaddr_t guest_paddr, size_t size);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <platform.h>
|
||||
#include <platform/debug.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
#if WITH_LIB_DEBUGLOG
|
||||
#include <lib/debuglog.h>
|
||||
|
||||
@@ -258,7 +258,7 @@ mx_status_t sys_process_create(mx_handle_t job_handle,
|
||||
ktrace_name(TAG_PROC_NAME, koid, 0, buf);
|
||||
|
||||
// Give arch-specific tracing a chance to record process creation.
|
||||
arch_trace_process_create(koid, &vmar_dispatcher->vmar()->aspace()->arch_aspace());
|
||||
arch_trace_process_create(koid, &vmar_dispatcher->vmar()->aspace()->arch_aspace().GetInnerAspace());
|
||||
|
||||
// Create a handle and attach the dispatcher to it
|
||||
HandleOwner proc_h(MakeHandle(mxtl::move(proc_dispatcher), proc_rights));
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include <arch/x86/mmu.h>
|
||||
#include <assert.h>
|
||||
#include <efi/boot-services.h>
|
||||
#include <err.h>
|
||||
#include <inttypes.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/memory_limit.h>
|
||||
#include <magenta/boot/multiboot.h>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#if WITH_DEV_PCIE
|
||||
|
||||
#include <arch/x86/feature.h>
|
||||
#include <inttypes.h>
|
||||
#include <dev/pcie_bus_driver.h>
|
||||
#include <dev/pcie_device.h>
|
||||
#include <mxtl/ref_ptr.h>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <dev/pcie_bus_driver.h>
|
||||
#include <dev/pcie_platform.h>
|
||||
#include <inttypes.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <lk/init.h>
|
||||
#include <magenta/syscalls/pci.h>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
#include <err.h>
|
||||
#include <inttypes.h>
|
||||
#include <reg.h>
|
||||
#include <trace.h>
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário