/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.x86;
import static org.jnode.vm.VirtualMemoryRegion.ACPI;
import org.jnode.annotation.MagicPermission;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VirtualMemoryRegion;
import org.jnode.vm.classmgr.VmIsolatedStatics;
import org.jnode.vm.classmgr.VmSharedStatics;
import org.jnode.vm.compiler.IMTCompiler;
import org.jnode.vm.facade.TypeSizeInfo;
import org.jnode.vm.scheduler.VmProcessor;
import org.jnode.vm.scheduler.VmScheduler;
import org.jnode.vm.x86.compiler.X86IMTCompiler32;
import org.vmmagic.pragma.UninterruptiblePragma;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Word;
/**
* Architecture description for the x86 (32-bit) architecture.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public final class VmX86Architecture32 extends VmX86Architecture {
/**
* Start address of the virtual memory region available to devices (3Gb)
*/
public static final int DEVICE_START = 0xC0000000;
/**
* End address of the virtual memory region available to devices (4Gb-4Mb)
*/
public static final int DEVICE_END = 0xFFC00000;
/**
* Start address of the virtual memory region available to ACPI (3Gb - 4Mb)
*/
public static final int ACPI_START = DEVICE_START - 0x400000;
/**
* Start address of the virtual memory region available to ACPI (3Gb)
*/
public static final int ACPI_END = DEVICE_START;
/**
* Start address of the virtual memory region available to the memory manager (256Mb).
* This address must be 4Mb aligned.
*/
public static final int AVAILABLE_START = 0x10000000;
/**
* End address of the virtual memory region available to the memory manager.
* This address must be 4Mb aligned.
*/
public static final int AVAILABLE_END = ACPI_START;
// Log of page size per region
private static final byte LOG_DEFAULT_PAGE_SIZE = 22;
private static final byte LOG_AVAILABLE_PAGE_SIZE = LOG_DEFAULT_PAGE_SIZE;
private static final byte LOG_HEAP_PAGE_SIZE = LOG_AVAILABLE_PAGE_SIZE;
private static final byte LOG_ACPI_PAGE_SIZE = 22;
private static final byte LOG_DEVICE_PAGE_SIZE = LOG_DEFAULT_PAGE_SIZE;
// Page sizes per region
private static final int AVAILABLE_PAGE_SIZE = 1 << LOG_AVAILABLE_PAGE_SIZE;
private static final int HEAP_PAGE_SIZE = 1 << LOG_HEAP_PAGE_SIZE;
private static final int ACPI_PAGE_SIZE = 1 << LOG_ACPI_PAGE_SIZE;
private static final int DEVICE_PAGE_SIZE = 1 << LOG_DEVICE_PAGE_SIZE;
/**
* Size of an object reference
*/
public static final int SLOT_SIZE = 4;
/**
* The IMT compiler
*/
private final X86IMTCompiler32 imtCompiler;
/**
* The type size information
*/
private final TypeSizeInfo typeSizeInfo;
/**
* The next physical page address to be mmaped
*/
private Word pageCursor;
/**
* Default page entry flags
*/
private static final int PF_DEFAULT = PF_PRESENT | PF_WRITE | PF_USER | PF_PSE;
/**
* Initialize this instance.
*/
public VmX86Architecture32() {
this("L1A");
}
/**
* Initialize this instance.
*
* @param compiler
*/
public VmX86Architecture32(String compiler) {
super(SLOT_SIZE, compiler);
this.imtCompiler = new X86IMTCompiler32();
this.typeSizeInfo = new TypeSizeInfo(1, 1, 2, 2, 1);
}
/**
* Create a processor instance for this architecture.
*
* @return The processor
*/
public VmProcessor createProcessor(int id, VmSharedStatics sharedStatics, VmIsolatedStatics isolatedStatics,
VmScheduler scheduler) {
return new VmX86Processor32(id, this, sharedStatics, isolatedStatics, scheduler, null);
}
public final IMTCompiler getIMTCompiler() {
return imtCompiler;
}
public final TypeSizeInfo getTypeSizeInfo() {
return typeSizeInfo;
}
public final byte getLogPageSize(int region) {
switch (region) {
case VirtualMemoryRegion.AVAILABLE:
return LOG_AVAILABLE_PAGE_SIZE;
case VirtualMemoryRegion.HEAP:
return LOG_HEAP_PAGE_SIZE;
case VirtualMemoryRegion.ACPI:
return LOG_ACPI_PAGE_SIZE;
case VirtualMemoryRegion.DEVICE:
return LOG_DEVICE_PAGE_SIZE;
default:
return LOG_DEFAULT_PAGE_SIZE;
}
}
public Address getEnd(int space) {
switch (space) {
case VirtualMemoryRegion.HEAP:
return Address.fromIntZeroExtend(AVAILABLE_END);
case VirtualMemoryRegion.AVAILABLE:
return Address.fromIntZeroExtend(AVAILABLE_END);
case VirtualMemoryRegion.DEVICE:
return Address.fromIntZeroExtend(DEVICE_END);
case VirtualMemoryRegion.ACPI:
return Address.fromIntZeroExtend(ACPI_END);
default:
return super.getEnd(space);
}
}
public Address getStart(int space) {
switch (space) {
case VirtualMemoryRegion.HEAP:
return Address.fromIntZeroExtend(BOOT_IMAGE_START);
case VirtualMemoryRegion.AVAILABLE:
return Address.fromIntZeroExtend(AVAILABLE_START);
case VirtualMemoryRegion.DEVICE:
return Address.fromIntZeroExtend(DEVICE_START);
case VirtualMemoryRegion.ACPI:
return Address.fromIntZeroExtend(ACPI_START);
default:
return super.getStart(space);
}
}
/**
* Map a region of the virtual memory space. Note that you cannot allocate
* memory in this memory, because it is used very early in the boot process.
*
* @param region Memory region
* @param start The start of the virtual memory region to map
* @param size The size of the virtual memory region to map
* @param physAddr The physical address to map the virtual address to. If this is
* Address.max(), free pages are used instead.
* @return true for success, false otherwise.
*/
public final boolean mmap(int region, Address start,
Extent size, Address physAddr) throws UninterruptiblePragma {
switch (region) {
case VirtualMemoryRegion.HEAP:
if (!physAddr.isMax()) {
return false;
}
break;
case VirtualMemoryRegion.ACPI:
if (physAddr.isMax()) {
return false;
}
break;
default:
return false;
}
final Word alignedStart = pageAlign(region, start.toWord(), false);
if (!alignedStart.EQ(start.toWord())) {
// Make adjustments on size & physAddr
final Word diff = start.sub(alignedStart).toWord();
start = alignedStart.toAddress();
size = size.add(diff);
if (!physAddr.isMax()) {
physAddr = physAddr.sub(diff);
}
}
size = pageAlign(region, size.toWord(), true).toExtent();
if (pageCursor.isZero()) {
Unsafe.debug("pageCursor is zero");
}
final Extent pageSize = getPageSize(region);
while (!size.isZero()) {
mapPage(start, physAddr, pageSize, (region == ACPI));
start = start.add(pageSize);
size = size.sub(pageSize);
if (!physAddr.isMax()) {
physAddr = physAddr.add(pageSize);
}
}
return true;
}
/**
* Unmap a region of the virtual memory space. Note that you cannot allocate
* memory in this memory, because it is used very early in the boot process.
*
* @param region Memory region
* @param start The start of the virtual memory region to unmap. This value is
* aligned down on pagesize.
* @param size The size of the virtual memory region to unmap. This value is
* aligned up on pagesize.
* @return true for success, false otherwise.
*/
public boolean munmap(int region, Address start, Extent size)
throws UninterruptiblePragma {
switch (region) {
case VirtualMemoryRegion.HEAP:
case VirtualMemoryRegion.ACPI:
break;
default:
return false;
}
final Word alignedStart = pageAlign(region, start.toWord(), false);
if (!alignedStart.EQ(start.toWord())) {
// Make adjustments on size & physAddr
final Word diff = start.sub(alignedStart).toWord();
start = alignedStart.toAddress();
size = size.add(diff);
}
size = pageAlign(region, size.toWord(), true).toExtent();
final Extent pageSize = getPageSize(region);
removeVirtualMMap(start.toWord(), start.add(size).toWord(), pageSize);
return true;
}
/**
* Map a page at the given virtual address.
*
* @param vmAddress
*/
private final void mapPage(Address vmAddress, Address physAddr, Extent pageSize, boolean debug) {
// Setup the pdir structures
final Word pdirIdx = vmAddress.toWord().rshl(22);
final Address pdirEntryPtr = UnsafeX86.getCR3().add(pdirIdx.lsh(2));
Word entry = pdirEntryPtr.loadWord();
if (entry.and(Word.fromIntZeroExtend(PF_PRESENT)).isZero()) {
final Word pagePtr;
if (physAddr.isMax()) {
// Get a free page
pagePtr = pageCursor;
pageCursor = pageCursor.add(pageSize);
} else {
pagePtr = physAddr.toWord();
}
// There is currently no present page, so do the mapping
entry = pagePtr.or(Word.fromIntZeroExtend(PF_DEFAULT));
pdirEntryPtr.store(entry);
if (debug) {
Unsafe.debug("mapPage ");
Unsafe.debug(entry);
Unsafe.debug('\n');
}
} else {
if (debug) {
Unsafe.debug("mapPage: page present\n");
}
}
}
protected void boot(boolean emptyMMap) {
Unsafe.debug("VmArchitecture32#boot\n");
dumpMultibootMMap();
pageCursor = getFirstAvailableHeapPage();
if (emptyMMap) {
// Remove all page mappings between AVAILABLE_START-END
final Word start = Word.fromIntZeroExtend(AVAILABLE_START);
final Word end = Word.fromIntZeroExtend(AVAILABLE_END);
final Extent pageSize = Extent.fromIntZeroExtend(AVAILABLE_PAGE_SIZE);
removeVirtualMMap(start, end, pageSize);
}
}
/**
* Remove all virtual memory mappings in a given address range.
*
* @param start
* @param end
*/
private final void removeVirtualMMap(Word start, Word end, Extent pageSize) {
final Address pdir = UnsafeX86.getCR3();
for (Word ptr = start; ptr.LT(end); ptr = ptr.add(pageSize)) {
final Word pdirIdx = ptr.rshl(22);
pdir.add(pdirIdx.lsh(2)).store(Word.zero());
}
}
}