/* * $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 org.jnode.system.resource.MemoryResource; import org.jnode.system.resource.ResourceManager; import org.jnode.system.resource.ResourceNotFreeException; import org.jnode.system.resource.ResourceOwner; import org.jnode.util.NumberUtils; import org.jnode.vm.VmSystem; import org.jnode.annotation.Inline; import org.jnode.annotation.KernelSpace; import org.jnode.annotation.MagicPermission; import org.jnode.annotation.Uninterruptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Extent; import org.vmmagic.unboxed.Word; /** * @author Ewout Prangsma (epr@users.sourceforge.net) */ @MagicPermission final class LocalAPIC { static final int ICR_DELIVERY_MODE_FIXED = 0x00 << 8; static final int ICR_DELIVERY_MODE_LOW_PRIO = 0x01 << 8; static final int ICR_DELIVERY_MODE_SMI = 0x02 << 8; static final int ICR_DELIVERY_MODE_NMI = 0x04 << 8; static final int ICR_DELIVERY_MODE_INIT = 0x05 << 8; static final int ICR_DELIVERY_MODE_STARTUP = 0x06 << 8; static final int ICR_DESTINATION_MODE_PHYSICAL = 0x00 << 11; static final int ICR_DESTINATION_MODE_LOGICAL = 0x01 << 11; static final int ICR_DELIVERY_STATUS_IDLE = 0x00 << 12; static final int ICR_DELIVERY_STATUS_PENDING = 0x01 << 12; static final int ICR_LEVEL_DEASSERT = 0x00 << 14; static final int ICR_LEVEL_ASSERT = 0x01 << 14; static final int ICR_TRIGGER_MODE_EDGE = 0x00 << 15; static final int ICR_TRIGGER_MODE_LEVEL = 0x01 << 15; static final int ICR_DESTINATION_SHORTHAND_NONE = 0x00 << 18; static final int ICR_DESTINATION_SHORTHAND_SELF = 0x01 << 18; static final int ICR_DESTINATION_SHORTHAND_ALL = 0x02 << 18; static final int ICR_DESTINATION_SHORTHAND_ALL_EX_SELF = 0x03 << 18; static final int SVR_APIC_DISABLED = 0x00 << 8; static final int SVR_APIC_ENABLED = 0x01 << 8; /** * Local APIC ID register */ static final int REG_APIC_ID = 0x0020; /** * Local APIC Version register (readonly) */ static final int REG_APIC_VERSION = 0x0030; /** * Task priority register */ static final int REG_TPR = 0x0080; /** * Arbitration priority register (readonly) */ static final int REG_APR = 0x0090; /** * Processor priority register (readonly) */ static final int REG_PPR = 0x00A0; /** * End of Interrupt register (writeonly) */ static final int REG_EOI = 0x00B0; /** * Spurious Interrupt vector register */ static final int REG_SVR = 0x00F0; /** * Error status register */ static final int REG_ESR = 0x0280; /** * Interrupt command register (high part) */ static final int REG_ICR_HIGH = 0x0310; /** * Interrupt command register (low part) */ static final int REG_ICR_LOW = 0x0300; /** * Memory region for local APIC */ private final MemoryResource mem; /** * Initialize this instance. * * @param rm * @param owner * @param ptr */ public LocalAPIC(ResourceManager rm, ResourceOwner owner, Address ptr) throws ResourceNotFreeException { mem = rm.claimMemoryResource(owner, ptr, Extent.fromIntZeroExtend(4096), ResourceManager.MEMMODE_NORMAL); } /** * Gets the Local APIC ID */ public final int getId() { return (mem.getInt(REG_APIC_ID) >> 24) & 0xFF; } /** * Is the local APIC enabled? */ public final boolean isEnabled() { return ((mem.getInt(REG_SVR) & SVR_APIC_ENABLED) != 0); } /** * Enable/disable the local APIC. */ public final void setEnabled(boolean enabled) { int v = mem.getInt(REG_SVR); if (enabled) { v |= SVR_APIC_ENABLED; } else { v &= ~SVR_APIC_ENABLED; } mem.setInt(REG_SVR, v); } /** * Send an INIT IPI to the processor with the given ID. * * @param dstId */ public final void sendInitIPI(int dstId, boolean levelAssert) { final int high = (dstId & 0xFF) << 24; int low = ICR_DELIVERY_MODE_INIT | ICR_TRIGGER_MODE_LEVEL; if (levelAssert) { low |= ICR_LEVEL_ASSERT; } mem.setInt(REG_ICR_HIGH, high); mem.setInt(REG_ICR_LOW, low); } /** * Send a STARTUP IPI to the processor with the given ID. * The processor will start at the given address. * * @param dstId * @param vector Address must be a 4K aligned address below 1Mb. */ public final void sendStartupIPI(int dstId, Address vector) { final int high = (dstId & 0xFF) << 24; int low = ICR_DELIVERY_MODE_STARTUP | ICR_DESTINATION_MODE_PHYSICAL | ICR_DESTINATION_SHORTHAND_NONE; int v = vector.toInt(); if ((v & 0xFFF00FFF) != 0) { throw new IllegalArgumentException( "Invalid vector 0x" + NumberUtils.hex(vector.toInt()) + " must be like 0x000vv000"); } low |= (v >> 12) & 0xFF; mem.setInt(REG_ICR_HIGH, high); mem.setInt(REG_ICR_LOW, low); } /** * Send a FIXED interrupt on the inter processor bus. * * @param dstId * @param dstShorthand * @param vector Interrupt vector */ @KernelSpace @Uninterruptible @Inline final void sendFixedIPI(int dstId, int dstShorthand, int vector) { // Wait until not busy while (isIPIPending()) { // Wait } final int high = (dstId & 0xFF) << 24; int low = ICR_DELIVERY_MODE_FIXED | ICR_DESTINATION_MODE_PHYSICAL | dstShorthand; low |= (vector & 0xFF); mem.setInt(REG_ICR_HIGH, high); mem.setInt(REG_ICR_LOW, low); } /** * Wait until the IPI is finished. */ public final void loopUntilNotBusy() { while (isIPIPending()) { VmSystem.loop(1); } } /** * Clear any error. */ public final void clearErrors() { mem.getInt(REG_SVR); mem.setInt(REG_ESR, 0); mem.getInt(REG_ESR); } /** * Get the current error flags. */ public final int getErrors() { return mem.getInt(REG_ESR); } /** * Gets the address if the EOI register. * * @return */ final Address getEOIAddress() { return mem.getAddress().add(Word.fromIntZeroExtend(REG_EOI)); } /** * Is an IPI pending? */ @KernelSpace @Uninterruptible @Inline public final boolean isIPIPending() { return ((mem.getInt(REG_ICR_LOW) & ICR_DELIVERY_STATUS_PENDING) != 0); } /** * Release all resources. */ final void release() { mem.release(); } }