/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the Archimulator multicore architectural simulator.
* <p>
* Archimulator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* Archimulator 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 General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.os;
import archimulator.common.BasicSimulationObject;
import archimulator.common.CPUExperiment;
import archimulator.common.Simulation;
import archimulator.common.SimulationObject;
import archimulator.isa.ArchitecturalRegisterFile;
import archimulator.isa.Memory;
import archimulator.isa.StaticInstruction;
import archimulator.os.event.SystemEvent;
import archimulator.os.signal.SignalAction;
import archimulator.util.buffer.CircularByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Kernel.
*
* @author Min Cai
*/
public class Kernel extends BasicSimulationObject<CPUExperiment, Simulation>
implements SimulationObject<CPUExperiment, Simulation> {
private List<Pipe> pipes;
private List<SystemEvent> systemEvents;
private List<SignalAction> signalActions;
private List<Context> contexts;
private List<Process> processes;
private SystemCallEmulation systemCallEmulation;
private long currentCycle;
/**
* Current/maximum process ID.
*/
public int currentPid = 1000;
/**
* Current/maximum memory ID.
*/
public int currentMemoryId = 0;
/**
* Current/maximum context ID.
*/
public int currentContextId = 0;
/**
* Current/maximum file descriptor ID.
*/
public int currentFd = 100;
/**
* Create a kernel.
*
* @param simulation the simulation object
*/
public Kernel(Simulation simulation) {
super(simulation);
this.pipes = new ArrayList<>();
this.systemEvents = new ArrayList<>();
this.signalActions = new ArrayList<>();
for (int i = 0; i < Kernel.MAX_SIGNAL; i++) {
this.signalActions.add(new SignalAction());
}
this.contexts = new ArrayList<>();
this.processes = new ArrayList<>();
this.systemCallEmulation = new SystemCallEmulation(this);
}
/**
* Get the process object from the specified process ID.
*
* @param id the process ID
* @return the process object matching the specified process ID
*/
public Process getProcessFromId(int id) {
for (Process process : this.processes) {
if (process.getId() == id) {
return process;
}
}
return null;
}
/**
* Get the context object from the specified context ID.
*
* @param id the context ID
* @return the context object matching the specified context ID
*/
public Context getContextFromId(int id) {
for (Context context : this.contexts) {
if (context.getId() == id) {
return context;
}
}
return null;
}
/**
* Get the context object from the specified process ID.
*
* @param processId the process ID
* @return the context object matching the specified process ID
*/
public Context getContextFromProcessId(int processId) {
for (Context context : this.contexts) {
if (context.getProcessId() == processId) {
return context;
}
}
return null;
}
/**
* Map the specified context to an idle thread.
*
* @param contextToMap the context to be mapped
* @param predicate the predicate
* @return a value indicating whether the mapping succeeds or not
*/
public boolean map(Context contextToMap, Predicate<Integer> predicate) {
if (contextToMap.getThreadId() != -1) {
throw new IllegalArgumentException();
}
for (int coreNum = 0; coreNum < this.getExperiment().getConfig().getNumCores(); coreNum++) {
for (int threadNum = 0; threadNum < this.getExperiment().getConfig().getNumThreadsPerCore(); threadNum++) {
int threadId = coreNum * this.getExperiment().getConfig().getNumThreadsPerCore() + threadNum;
boolean hasMapped = false;
for (Context context : this.getContexts()) {
if (context.getThreadId() == threadId) {
hasMapped = true;
break;
}
}
if (!hasMapped && predicate.test(threadId)) {
contextToMap.setThreadId(threadId);
return true;
}
}
}
return false;
}
/**
* Schedule the specified system event.
*
* @param event the system event to be scheduled
*/
public void scheduleSystemEvent(SystemEvent event) {
this.systemEvents.add(event);
}
/**
* Process the pending list of system events.
*/
public void processSystemEvents() {
for (Iterator<SystemEvent> it = this.systemEvents.iterator(); it.hasNext(); ) {
SystemEvent e = it.next();
if ((e.getContext().getState() == ContextState.RUNNING || e.getContext().getState() == ContextState.BLOCKED) && !e.getContext().isSpeculative() && e.needProcess()) {
it.remove();
e.process();
}
}
}
/**
* Process the pending list of signals.
*/
public void processSignals() {
for (Context context : this.contexts) {
if ((context.getState() == ContextState.RUNNING || context.getState() == ContextState.BLOCKED) && !context.isSpeculative()) {
for (int signal = 1; signal <= MAX_SIGNAL; signal++) {
if (this.mustProcessSignal(context, signal)) {
this.runSignalHandler(context, signal);
}
}
}
}
}
/**
* Create a pipe for the specified array of two file descriptor numbers.
*
* @param fileDescriptors the array of two descriptor numbers
*/
public void createPipe(int[] fileDescriptors) {
fileDescriptors[0] = this.currentFd++;
fileDescriptors[1] = this.currentFd++;
this.pipes.add(new Pipe(fileDescriptors));
}
/**
* Close the pipes containing the specified file descriptor number.
*
* @param fileDescriptor the file descriptor number
*/
public void closePipe(int fileDescriptor) {
for (Iterator<Pipe> it = this.pipes.iterator(); it.hasNext(); ) {
Pipe pipe = it.next();
if (pipe.getFileDescriptors()[0] == fileDescriptor) {
pipe.getFileDescriptors()[0] = -1;
}
if (pipe.getFileDescriptors()[1] == fileDescriptor) {
pipe.getFileDescriptors()[1] = -1;
}
if (pipe.getFileDescriptors()[0] == -1 && pipe.getFileDescriptors()[1] == -1) {
it.remove();
}
}
}
/**
* Get the read buffer for the specified file descriptor number.
*
* @param fileDescriptor the file descriptor number
* @return the read buffer for the specified file descriptor number
*/
public CircularByteBuffer getReadBuffer(int fileDescriptor) {
return this.getBuffer(fileDescriptor, 0);
}
/**
* Get the write buffer for the specified file descriptor number.
*
* @param fileDescriptor the file descriptor number
* @return the write buffer for the specified file descriptor number
*/
public CircularByteBuffer getWriteBuffer(int fileDescriptor) {
return this.getBuffer(fileDescriptor, 1);
}
/**
* Get the circular buffer for the specified file descriptor number and index.
*
* @param fileDescriptor the file descriptor
* @param index the index
* @return the circular buffer matching the specified file descriptor number and index
*/
private CircularByteBuffer getBuffer(int fileDescriptor, int index) {
for (Pipe pipe : this.pipes) {
if (pipe.getFileDescriptors()[index] == fileDescriptor) {
return pipe.getBuffer();
}
}
return null;
}
/**
* Run the signal handler for the specified context and signal.
*
* @param context the context
* @param signal the signal
*/
public void runSignalHandler(Context context, int signal) {
try {
if (this.signalActions.get(signal - 1).getHandler() == 0) {
throw new RuntimeException();
}
// System.out.printf("%s 0x%08x: executing signal %d handler\n", context.getThread().getName(), signalActions[signal - 1].getHandler(), signal);
context.getSignalMasks().getPending().clear(signal);
ArchitecturalRegisterFile oldRegisterFile = (ArchitecturalRegisterFile) context.getRegisterFile().clone();
context.getRegisterFile().setGpr(ArchitecturalRegisterFile.REGISTER_A0, signal);
context.getRegisterFile().setGpr(ArchitecturalRegisterFile.REGISTER_T9, this.signalActions.get(signal - 1).getHandler());
context.getRegisterFile().setGpr(ArchitecturalRegisterFile.REGISTER_RA, 0xffffffff);
context.getRegisterFile().setNpc(this.signalActions.get(signal - 1).getHandler());
context.getRegisterFile().setNnpc(context.getRegisterFile().getNpc() + 4);
while (context.getState() == ContextState.RUNNING && context.getRegisterFile().getNpc() != 0xffffffff) {
StaticInstruction.execute(context.decodeNextInstruction(), context);
}
context.setRegisterFile(oldRegisterFile);
// System.out.printf("%s 0x%08x: return from signal %d handler\n", context.getThread().getName(), context.getRegisterFile().getNpc(), signal);
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
/**
* Get a value indicating whether the specified signal must be processed for the specified context or not.
*
* @param context the context
* @param signal the signal
* @return a value indicating whether the specified signal must be processed for the specified context or not
*/
public boolean mustProcessSignal(Context context, int signal) {
return context.getSignalMasks().getPending().contains(signal) && !context.getSignalMasks().getBlocked().contains(signal);
}
/**
* Advance one cycle.
*/
public void advanceOneCycle() {
if (this.currentCycle % 1000 == 0) {
this.processSystemEvents();
this.processSignals();
}
this.currentCycle++;
}
/**
* Get the list of signal actions.
*
* @return the list of signal actions
*/
public List<SignalAction> getSignalActions() {
return signalActions;
}
/**
* Get the list of contexts.
*
* @return the list of contexts
*/
public List<Context> getContexts() {
return contexts;
}
/**
* Get the list of processes.
*
* @return the list of processes
*/
public List<Process> getProcesses() {
return processes;
}
/**
* Get the list of memories.
*
* @return the list of memories
*/
public List<Memory> getMemories() {
return processes.stream().map(Process::getMemory).collect(Collectors.toList());
}
/**
* Get the current cycle.
*
* @return the current cycle
*/
public long getCurrentCycle() {
return currentCycle;
}
/**
* Get the system call emulation object.
*
* @return the system call emulation object
*/
public SystemCallEmulation getSystemCallEmulation() {
return systemCallEmulation;
}
/**
* Get the name of the kernel.
*
* @return the name of the kernel
*/
@Override
public String getName() {
return "kernel";
}
/**
* Maximum signal.
*/
public static final int MAX_SIGNAL = 64;
}