/*
* $Id$
*
* Copyright (C) 2003-2015 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 java.nio.ByteOrder;
import java.util.HashMap;
import org.jnode.annotation.Internal;
import org.jnode.annotation.MagicPermission;
import org.jnode.assembler.x86.X86Constants;
import org.jnode.bootlog.BootLogInstance;
import org.jnode.system.resource.ResourceManager;
import org.jnode.system.resource.ResourceNotFreeException;
import org.jnode.system.resource.ResourceOwner;
import org.jnode.vm.BaseVmArchitecture;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VmMagic;
import org.jnode.vm.VmMultiMediaSupport;
import org.jnode.vm.VmSystem;
import org.jnode.vm.classmgr.VmIsolatedStatics;
import org.jnode.vm.classmgr.VmSharedStatics;
import org.jnode.vm.compiler.NativeCodeCompiler;
import org.jnode.vm.facade.MemoryMapEntry;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.scheduler.IRQManager;
import org.jnode.vm.scheduler.VmProcessor;
import org.jnode.vm.scheduler.VmScheduler;
import org.jnode.vm.x86.compiler.l1a.X86Level1ACompiler;
import org.jnode.vm.x86.compiler.l1b.X86Level1BCompiler;
import org.jnode.vm.x86.compiler.l2.X86Level2Compiler;
import org.jnode.vm.x86.compiler.stub.X86StubCompiler;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
/**
* Architecture descriptor for the Intel X86 architecture.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public abstract class VmX86Architecture extends BaseVmArchitecture {
/**
* Start address of the boot image (1Mb)
*/
public static final int BOOT_IMAGE_START = 0x00100000;
// Page entry flags
protected static final int PF_PRESENT = 0x00000001;
protected static final int PF_WRITE = 0x00000002;
protected static final int PF_USER = 0x00000004;
protected static final int PF_PWT = 0x00000008;
protected static final int PF_PCD = 0x00000010;
protected static final int PF_ACCESSED = 0x00000020;
protected static final int PF_DIRTY = 0x00000040;
protected static final int PF_PSE = 0x00000080;
protected static final int MBMMAP_BASEADDR = 0; // 64-bit base address
protected static final int MBMMAP_LENGTH = 8; // 64-bit length
protected static final int MBMMAP_TYPE = 16; // 32-bit type
protected static final int MBMMAP_ESIZE = 20;
// Values for MBMMAP_TYPE field
protected static final int MMAP_TYPE_MEMORY = 1; // Available memory
protected static final int MMAP_TYPE_RESERVED = 2; // Reserved memory
protected static final int MMAP_TYPE_ACPI = 3; // ACPI reclaim memory
protected static final int MMAP_TYPE_NVS = 4; // ACPI NVS memory
protected static final int MMAP_TYPE_UNUSABLE = 5; // Memory with errors
// found in it
/**
* The compilers
*/
private final NativeCodeCompiler[] compilers;
/**
* The compilers under test
*/
private final NativeCodeCompiler[] testCompilers;
/**
* The local APIC accessor, if any
*/
private LocalAPIC localAPIC;
/**
* The MP configuration table
*/
private MPConfigTable mpConfigTable;
/**
* Programmable interrupt controller
*/
private PIC8259A pic8259a;
/**
* The boot processor
*/
private transient VmX86Processor bootProcessor;
/**
* The centralized irq manager
*/
private transient X86IRQManager irqManager;
/**
* Initialize this instance using the default compiler.
*/
public VmX86Architecture(int referenceSize) {
this(referenceSize, "L1A");
}
/**
* Initialize this instance.
*
* @param compiler the name of the compiler to use as standard. If
* the supplied name is {@code null} or doesn't match (case insensitively)
* one of the known names, the default compiler will be used.
*/
public VmX86Architecture(int referenceSize, String compiler) {
super(referenceSize, new VmX86StackReader(referenceSize));
this.compilers = new NativeCodeCompiler[2];
this.compilers[0] = new X86StubCompiler();
// Compare insensitively, producing a warning if the user selects
// an unknown compiler, and using a default where appropriate.
if (compiler != null && compiler.length() > 0 &&
!compiler.equalsIgnoreCase("default")) {
if ("L1B".equalsIgnoreCase(compiler)) {
this.compilers[1] = new X86Level1BCompiler();
} else if ("L1A".equalsIgnoreCase(compiler)) {
this.compilers[1] = new X86Level1ACompiler();
} else {
BootLogInstance.get().warn("JNode native compiler '" + compiler + "' is unknown.");
}
}
if (this.compilers[1] == null) {
BootLogInstance.get().warn("JNode native compiler defaulting to 'L1A'");
this.compilers[1] = new X86Level1ACompiler();
}
this.testCompilers = new NativeCodeCompiler[1];
this.testCompilers[0] = new X86Level2Compiler();
}
/**
* Gets the name of this architecture.
*
* @return name
*/
public final String getName() {
return "x86";
}
/**
* Gets the full name of this architecture, including operating mode.
*
* @return Name
*/
public String getFullName() {
if (getReferenceSize() == 4) {
return getName() + "-32";
} else {
return getName() + "-64";
}
}
/**
* Gets the byte ordering of this architecture.
*
* @return ByteOrder
*/
public final ByteOrder getByteOrder() {
return ByteOrder.LITTLE_ENDIAN;
}
/**
* Gets the current operating mode; i.e. 32 or 64 bit mode.
*
* @return mode
*/
public final X86Constants.Mode getMode() {
if (getReferenceSize() == 4) {
return X86Constants.Mode.CODE32;
} else {
return X86Constants.Mode.CODE64;
}
}
/**
* Gets all compilers for this architecture.
*
* @return The compilers, sorted by optimization level, from least
* optimizations to most optimizations.
*/
public final NativeCodeCompiler[] getCompilers() {
return compilers;
}
/**
* Gets all test compilers for this architecture.
*
* @return The compilers, sorted by optimization level, from least
* optimizations to most optimizations.
*/
public final NativeCodeCompiler[] getTestCompilers() {
return testCompilers;
}
/**
* @see org.jnode.vm.BaseVmArchitecture#initializeProcessors(ResourceManager)
*/
protected final void initializeProcessors(ResourceManager rm) {
// Mark current cpu as bootprocessor
final VmX86Processor bootCpu = (VmX86Processor) VmMagic.currentProcessor();
this.bootProcessor = bootCpu;
bootCpu.setBootProcessor(true);
// Initialize HyperV
initializeHyperV();
final String cmdLine = VmSystem.getCmdLine();
if (cmdLine.contains("mp=no")) {
return;
}
//
final MPFloatingPointerStructure mp = MPFloatingPointerStructure.find(
rm, ResourceOwner.SYSTEM);
if (mp == null) {
BootLogInstance.get().info("No MP table found");
// No MP table found.
return;
}
try {
BootLogInstance.get().info("Found " + mp);
this.mpConfigTable = mp.getMPConfigTable();
} finally {
mp.release();
}
if (mpConfigTable == null) {
return;
}
mpConfigTable.dump(System.out);
final ResourceOwner owner = ResourceOwner.SYSTEM;
try {
// Create the local APIC accessor
localAPIC = new LocalAPIC(rm, owner, mpConfigTable
.getLocalApicAddress());
} catch (ResourceNotFreeException ex) {
BootLogInstance.get().error("Cannot claim APIC region");
return;
}
// Set the APIC reference of the current (bootstrap) processor
bootCpu.setApic(localAPIC);
bootCpu.loadAndSetApicID();
// Find & initialize this I/O APIC.
for (MPEntry entry : mpConfigTable.entries()) {
if (entry instanceof MPIOAPICEntry) {
final MPIOAPICEntry apicEntry = (MPIOAPICEntry) entry;
if (apicEntry.getFlags() != 0) {
try {
// We found an enabled I/O APIC.
final IOAPIC ioAPIC = new IOAPIC(rm, owner, apicEntry
.getAddress());
ioAPIC.dump(System.out);
break;
} catch (ResourceNotFreeException ex) {
BootLogInstance.get().error("Cannot claim I/O APIC region ", ex);
}
}
}
}
try {
// Detect Hyper threading on current (bootstrap) processor
VmX86Processor.detectAndstartLogicalProcessors(rm);
} catch (ResourceNotFreeException ex) {
BootLogInstance.get().error("Cannot claim region for logical processor startup",
ex);
}
// Find all physical AP processors
final X86CpuID cpuId = (X86CpuID) bootCpu.getCPUID();
final HashMap<Integer, MPProcessorEntry> physCpus = new HashMap<Integer, MPProcessorEntry>();
for (MPEntry e : mpConfigTable.entries()) {
if (e.getEntryType() == 0) {
final MPProcessorEntry cpuEntry = (MPProcessorEntry) e;
if (cpuEntry.isEnabled() && !cpuEntry.isBootstrap()) {
// Check if it is a physical CPU
final int apicId = cpuEntry.getApicID();
int physId = cpuId.getPhysicalPackageId(apicId);
// This algorithme is based on the specification
// that physical processors are listed before logical
// processors.
if (!physCpus.containsKey(physId)) {
// New physical CPU found
physCpus.put(physId, cpuEntry);
}
}
}
}
// Start all physical AP processors
for (MPProcessorEntry cpuEntry : physCpus.values()) {
final int apicId = cpuEntry.getApicID();
// New CPU
final VmX86Processor newCpu = (VmX86Processor) createProcessor(
apicId, VmUtils.getVm().getSharedStatics(), bootCpu
.getIsolatedStatics(), bootCpu.getScheduler());
initX86Processor(newCpu);
try {
newCpu.startup(rm);
} catch (ResourceNotFreeException ex) {
BootLogInstance.get().error("Cannot claim region for processor startup", ex);
}
}
// If there is more then one CPU, start sending timeslice interrupts now
BootLogInstance.get().info("Activating timeslice interrupts");
bootCpu.activateTimeSliceInterrupts();
}
/**
* Create a processor instance for this architecture.
*
* @return The processor
*/
public abstract VmProcessor createProcessor(int id,
VmSharedStatics sharedStatics, VmIsolatedStatics isolatedStatics,
VmScheduler scheduler);
@Override
@Internal
public final IRQManager createIRQManager(VmProcessor processor) {
synchronized (this) {
// Create PIC if not available
if (pic8259a == null) {
pic8259a = new PIC8259A();
}
if (irqManager == null) {
irqManager = new X86IRQManager(bootProcessor, pic8259a);
}
}
return irqManager;
}
/**
* Initialize a processor wrt. APIC and add it to the list of processors.
*
* @param cpu
*/
final void initX86Processor(VmX86Processor cpu) {
cpu.setApic(localAPIC);
super.addProcessor(cpu);
}
/**
* Print the multiboot memory map to Unsafe.debug.
*/
protected final void dumpMultibootMMap() {
final int cnt = UnsafeX86.getMultibootMMapLength();
Address mmap = UnsafeX86.getMultibootMMap();
Unsafe.debug("Memory map\n");
for (int i = 0; i < cnt; i++) {
long base = mmap
.loadLong(Offset.fromIntZeroExtend(MBMMAP_BASEADDR));
long length = mmap
.loadLong(Offset.fromIntZeroExtend(MBMMAP_LENGTH));
int type = mmap.loadInt(Offset.fromIntZeroExtend(MBMMAP_TYPE));
mmap = mmap.add(MBMMAP_ESIZE);
Unsafe.debug(mmapTypeToString(type));
Unsafe.debug(base);
Unsafe.debug(" - ");
Unsafe.debug(base + length - 1);
Unsafe.debug('\n');
}
}
/**
* Convert an mmap type into a human readable string.
*
* @param type
* @return
*/
private final String mmapTypeToString(int type) {
switch (type) {
case MMAP_TYPE_MEMORY:
return "Available ";
case MMAP_TYPE_RESERVED:
return "Reserved ";
case MMAP_TYPE_ACPI:
return "ACPI reclaim ";
case MMAP_TYPE_NVS:
return "ACPI NVS ";
case MMAP_TYPE_UNUSABLE:
return "Unusable ";
default:
return "Undefined ";
}
}
/**
* @see org.jnode.vm.BaseVmArchitecture#createMemoryMap()
*/
protected MemoryMapEntry[] createMemoryMap() {
final int cnt = UnsafeX86.getMultibootMMapLength();
final MemoryMapEntry[] map = new MemoryMapEntry[cnt];
Address mmap = UnsafeX86.getMultibootMMap();
for (int i = 0; i < cnt; i++) {
long base = mmap
.loadLong(Offset.fromIntZeroExtend(MBMMAP_BASEADDR));
long length = mmap
.loadLong(Offset.fromIntZeroExtend(MBMMAP_LENGTH));
int type = mmap.loadInt(Offset.fromIntZeroExtend(MBMMAP_TYPE));
mmap = mmap.add(MBMMAP_ESIZE);
map[i] = new X86MemoryMapEntry(Address.fromLong(base), Extent
.fromLong(length), type);
}
return map;
}
/**
* @see org.jnode.vm.BaseVmArchitecture#createMultiMediaSupport()
*/
protected VmMultiMediaSupport createMultiMediaSupport() {
final X86CpuID id = (X86CpuID) VmProcessor.current().getCPUID();
if (id.hasMMX()) {
return new MMXMultiMediaSupport();
} else {
return super.createMultiMediaSupport();
}
}
/**
* Identify ourselves in HyperV (when that is detected)
*/
void initializeHyperV() {
final X86CpuID id = (X86CpuID) VmProcessor.current().getCPUID();
if (!id.detectHyperV())
return;
Unsafe.debug("Initializing HyperV");
long guestOsId = (0x29L << 48);
UnsafeX86.writeMSR(Word.fromIntZeroExtend(HyperV.HV_X64_MSR_GUEST_OS_ID), guestOsId);
Unsafe.debug("Initialized Hyper-V guest OS ID");
}
}