mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-20 04:48:53 +00:00
Update Global to call driver initialization routines for different drivers. Devices are enumerated during startup regardless of what hardware you are using. Update FrodeTest workspace to run via new NetworkDevice interface Update Builder.cs so that QEMU gets called with only either "-net tap" or "-net user", as these two options conflict Internalized most of the TCP/IP stack leaving only the User needed classes available to use. Commented most of TCP/IP stack public methods, and cleaned out code a little bit.
474 lines
23 KiB
C#
474 lines
23 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using Microsoft.Win32;
|
|
using Indy.IL2CPU;
|
|
using Indy.IL2CPU.IL.X86;
|
|
using IL2CPU;
|
|
using System.IO.Pipes;
|
|
|
|
namespace Cosmos.Compiler.Builder {
|
|
|
|
public class Builder {
|
|
public string BuildPath;
|
|
public readonly string ToolsPath;
|
|
public readonly string AsmPath;
|
|
public readonly Engine Engine = new Engine();
|
|
public bool UseInternalAssembler = false;
|
|
public Builder() {
|
|
BuildPath = GetBuildPath();
|
|
ToolsPath = BuildPath + @"Tools\";
|
|
AsmPath = ToolsPath + @"asm\";
|
|
// MtW: leave this here, otherwise VS wont copy required dependencies!
|
|
typeof(X86OpCodeMap).Equals(null);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the path to the Build directory.
|
|
/// First looks in the .xml file in %AppData%\Cosmos.
|
|
/// Secondly searches Registry.
|
|
/// If no match found there either it will use the Current Directory to calculate the path to the Build directory.
|
|
/// </summary>
|
|
/// <returns>Full path to the Build directory.</returns>
|
|
protected static string GetBuildPath()
|
|
{
|
|
try
|
|
{
|
|
//string xResult = "";
|
|
Options.Load();
|
|
|
|
//Primary look in .XML
|
|
if (!String.IsNullOrEmpty(Options.BuildPath))
|
|
{
|
|
if (Directory.Exists(Options.BuildPath))
|
|
return Options.BuildPath;
|
|
else
|
|
Options.BuildPath = String.Empty; //Reset the path if it doesn't exist.
|
|
}
|
|
|
|
//Fallback to Registry
|
|
if (String.IsNullOrEmpty(Options.BuildPath))
|
|
{
|
|
RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Cosmos");
|
|
if (key != null)
|
|
{
|
|
var xRegPath = key.GetValue("Build Path", String.Empty).ToString();
|
|
if (!String.IsNullOrEmpty(xRegPath))
|
|
{
|
|
if (!Directory.Exists(xRegPath))
|
|
throw new DirectoryNotFoundException(
|
|
"Found path to Build Path in registry, but directory does not exist.");
|
|
Options.BuildPath = xRegPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Fallback to calculating from current dir
|
|
if (string.IsNullOrEmpty(Options.BuildPath))
|
|
{
|
|
var xCalculatedPath = Directory.GetCurrentDirectory().ToLowerInvariant();
|
|
int xPos = xCalculatedPath.LastIndexOf("source");
|
|
if (xPos == -1)
|
|
{
|
|
xPos = xCalculatedPath.LastIndexOf("buildoutput");
|
|
if(xPos == -1) {
|
|
throw new Exception("Unable to find directory named 'BuildOutput' or 'Source' when using CurrentDirectory. (CurrentDirectory = '" +Environment.CurrentDirectory + "')");
|
|
}
|
|
}
|
|
xCalculatedPath = xCalculatedPath.Substring(0, xPos) + @"Build\";
|
|
Options.BuildPath = xCalculatedPath;
|
|
}
|
|
|
|
//If path not in .xml, Registry and also unable to calculate
|
|
if (string.IsNullOrEmpty(Options.BuildPath))
|
|
throw new DirectoryNotFoundException("Unable to find Cosmos Build Path!");
|
|
|
|
//Make sure path has a trailing slash
|
|
if (!Options.BuildPath.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
|
Options.BuildPath += Path.DirectorySeparatorChar;
|
|
|
|
Options.Save();
|
|
return Options.BuildPath;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
throw new Exception("Error while getting Cosmos Build Path!", E);
|
|
}
|
|
}
|
|
|
|
protected void RemoveFile(string aPathname)
|
|
{
|
|
if (File.Exists(aPathname))
|
|
{
|
|
RemoveReadOnlyAttribute(aPathname);
|
|
File.Delete(aPathname);
|
|
}
|
|
}
|
|
|
|
protected void CopyFile(string aFrom, string aTo)
|
|
{
|
|
string xDir = Path.GetDirectoryName(aTo);
|
|
if (!Directory.Exists(xDir))
|
|
{
|
|
Directory.CreateDirectory(xDir);
|
|
}
|
|
File.Copy(aFrom, aTo);
|
|
}
|
|
|
|
protected void RemoveReadOnlyAttribute(string aPathname)
|
|
{
|
|
var xAttribs = File.GetAttributes(aPathname);
|
|
if ((xAttribs & FileAttributes.ReadOnly) > 0)
|
|
{
|
|
// This works because we only do this if Read only is already set
|
|
File.SetAttributes(aPathname, xAttribs ^ FileAttributes.ReadOnly);
|
|
}
|
|
}
|
|
|
|
public void MakeISO() {
|
|
string xPath = BuildPath + @"ISO\";
|
|
RemoveFile(BuildPath + "cosmos.iso");
|
|
RemoveFile(xPath + "output.bin");
|
|
CopyFile(BuildPath + "output.bin", xPath + "output.bin");
|
|
// From TFS its read only, mkisofs doesnt like that
|
|
RemoveReadOnlyAttribute(xPath + "isolinux.bin");
|
|
Global.Call(ToolsPath + @"mkisofs.exe", @"-R -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -o ..\Cosmos.iso .", xPath);
|
|
}
|
|
|
|
public event Action CompileCompleted;
|
|
public event Action<int, int> CompilingMethods;
|
|
public event Action<int, int> CompilingStaticFields;
|
|
public event Action<LogSeverityEnum, string> LogMessage;
|
|
private void OnCompilingMethods(int aCur, int aMax) {
|
|
if (CompilingMethods != null) { CompilingMethods(aCur, aMax); }
|
|
}
|
|
|
|
private void OnCompilingStaticFields(int aCur, int aMax) {
|
|
if (CompilingStaticFields != null) { CompilingStaticFields(aCur, aMax); }
|
|
}
|
|
|
|
private void OnLogMessage(LogSeverityEnum aSeverity, string aMessage) {
|
|
if (aSeverity == LogSeverityEnum.Informational) {
|
|
return; }
|
|
if (LogMessage != null) { LogMessage(aSeverity, aMessage); }
|
|
}
|
|
|
|
protected void ThreadExecute(object aParam) {
|
|
var xParam = (PassedEngineValue)aParam;
|
|
Engine.TraceAssemblies = xParam.TraceAssemblies;
|
|
Engine.CompilingMethods += OnCompilingMethods;
|
|
Engine.CompilingStaticFields += OnCompilingStaticFields;
|
|
try {
|
|
Engine.DebugLog += OnLogMessage;
|
|
Engine.Execute(xParam.aAssembly,
|
|
xParam.aTargetPlatform,
|
|
g => Path.Combine(AsmPath, g + ".asm"),
|
|
xParam.aPlugs,
|
|
xParam.aDebugMode,
|
|
xParam.GDBDebug,
|
|
1,
|
|
xParam.aOutputDir,
|
|
UseInternalAssembler);
|
|
}catch(Exception E) {
|
|
OnLogMessage(LogSeverityEnum.Error, E.ToString());
|
|
}
|
|
CompileCompleted.Invoke();
|
|
}
|
|
|
|
private string GetMonoExeFilename() {
|
|
return Path.Combine(Path.Combine(Path.Combine(Path.Combine(GetBuildPath(),
|
|
"Tools"),
|
|
"Mono"),
|
|
"bin"),
|
|
"mono.exe");
|
|
}
|
|
|
|
private string GetMonoLibDir() {
|
|
return Path.Combine(Path.Combine(Path.Combine(GetBuildPath(),
|
|
"Tools"),
|
|
"Mono"),
|
|
"lib");
|
|
}
|
|
|
|
protected void MonoThreadExecute(object aParam) {
|
|
var xParam = (PassedEngineValue)aParam;
|
|
var xExeParamsBuilder = new StringBuilder();
|
|
xExeParamsBuilder.AppendFormat("\"{0}\" ",
|
|
typeof(IL2CPU.Program).Assembly.Location);
|
|
xExeParamsBuilder.AppendFormat("/assembly \"{0}\" ",
|
|
xParam.aAssembly);
|
|
if (xParam.aOutputDir.EndsWith("\\")) {
|
|
xExeParamsBuilder.AppendFormat("/output \"{0}\\\" ",
|
|
xParam.aOutputDir);
|
|
} else {
|
|
xExeParamsBuilder.AppendFormat("/output \"{0}\" ",
|
|
xParam.aOutputDir);
|
|
}
|
|
xExeParamsBuilder.AppendFormat("/trace \"{0}\" ",
|
|
xParam.TraceAssemblies.ToString());
|
|
xExeParamsBuilder.AppendFormat("/target X86 ");
|
|
xExeParamsBuilder.AppendFormat("/comport 1 ");
|
|
foreach (var xPlug in xParam.aPlugs) {
|
|
xExeParamsBuilder.AppendFormat("/plug \"{0}\" ",
|
|
xPlug);
|
|
}
|
|
xExeParamsBuilder.AppendFormat("/logpipe CosmosCompileLog");
|
|
//var xProcess = Process.Start(@"C:\Program Files\Mono-2.0.1\bin\mono.exe",
|
|
// xExeParamsBuilder.ToString());
|
|
var xProcessStartInfo = new ProcessStartInfo(GetMonoExeFilename());
|
|
//var xProcessStartInfo = new ProcessStartInfo(typeof(IL2CPU.Program).Assembly.Location);
|
|
xProcessStartInfo.Arguments = xExeParamsBuilder.ToString();
|
|
xProcessStartInfo.RedirectStandardError = true;
|
|
xProcessStartInfo.RedirectStandardInput = true;
|
|
xProcessStartInfo.RedirectStandardOutput = true;
|
|
xProcessStartInfo.EnvironmentVariables.Add("MONO_PATH", GetMonoLibDir());
|
|
xProcessStartInfo.UseShellExecute = false;
|
|
using (var xLogStream= new System.IO.Pipes.NamedPipeServerStream("CosmosCompileLog",PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough)) {
|
|
AutoResetEvent xWaitForConEvent = new AutoResetEvent(false);
|
|
var xAsyncCookie = xLogStream.BeginWaitForConnection(delegate { xWaitForConEvent.Set(); }, null);
|
|
var xProcess = Process.Start(xProcessStartInfo);
|
|
try {
|
|
while (!xProcess.HasExited) {
|
|
if (xWaitForConEvent.WaitOne(100)) {
|
|
break;
|
|
}
|
|
}
|
|
xLogStream.EndWaitForConnection(xAsyncCookie);
|
|
bool xRunning = true;
|
|
do {
|
|
//while (!xLogStream.DataAvailable) { Thread.Sleep(15); }
|
|
var xBuff = new byte[1];
|
|
if (xLogStream.Read(xBuff, 0, 1) != 1) { throw new Exception("No Command received!"); }
|
|
switch (xBuff[0]) {
|
|
case (byte)LogChannelCommand.CompilingMethods: {
|
|
xBuff = new byte[8];
|
|
if (xLogStream.Read(xBuff, 0, 8) != 8) { throw new Exception("No complete CompilingMethods data received!"); }
|
|
var xCurrent = BitConverter.ToInt32(xBuff, 0);
|
|
var xMax = BitConverter.ToInt32(xBuff, 4);
|
|
OnCompilingMethods(xCurrent, xMax);
|
|
break;
|
|
}
|
|
case (byte)LogChannelCommand.CompilingFields: {
|
|
xBuff = new byte[8];
|
|
if (xLogStream.Read(xBuff, 0, 8) != 8) { throw new Exception("No complete CompilingFields data received!"); }
|
|
var xCurrent = BitConverter.ToInt32(xBuff, 0);
|
|
var xMax = BitConverter.ToInt32(xBuff, 4);
|
|
OnCompilingStaticFields(xCurrent, xMax);
|
|
break;
|
|
}
|
|
case (byte)LogChannelCommand.LogMessage: {
|
|
xBuff = new byte[5];
|
|
if (xLogStream.Read(xBuff, 0, 5) != 5) { throw new Exception("No complete LogMessage data received!"); }
|
|
var xSeverity = (LogSeverityEnum)xBuff[0];
|
|
var xStringLength = BitConverter.ToInt32(xBuff, 1);
|
|
xBuff = new byte[xStringLength];
|
|
if (xLogStream.Read(xBuff, 0, xStringLength) != xStringLength) { throw new Exception("No complete LogMessage data received! (2)"); }
|
|
var xMessage = Encoding.Unicode.GetString(xBuff);
|
|
OnLogMessage(xSeverity, xMessage);
|
|
break;
|
|
}
|
|
case (byte)LogChannelCommand.EndOfProcessing: {
|
|
xRunning = false;
|
|
break;
|
|
}
|
|
}
|
|
} while (!xProcess.HasExited && xRunning);
|
|
} catch (Exception E) {
|
|
if (xLogStream.IsConnected && !xProcess.HasExited) {
|
|
throw new Exception("Error while processing log command", E);
|
|
}
|
|
}
|
|
if (!xProcess.HasExited) { xProcess.Kill(); }
|
|
}
|
|
// at the end:
|
|
CompileCompleted.Invoke();
|
|
}
|
|
|
|
// MtW: added as field, so that it can be reused by the test runner
|
|
public Assembly TargetAssembly = Assembly.GetEntryAssembly();
|
|
|
|
public string[] GetPlugs()
|
|
{
|
|
string[] xPlugs;
|
|
if (File.Exists(Path.Combine(Path.Combine(ToolsPath, "Cosmos.Kernel.Plugs"), "Cosmos.Kernel.Plugs.dll")))
|
|
{
|
|
xPlugs = new string[] {
|
|
Path.Combine(Path.Combine(ToolsPath, "Cosmos.Kernel.Plugs"), "Cosmos.Kernel.Plugs.dll"),
|
|
Path.Combine(Path.Combine(ToolsPath, "Cosmos.Hardware.Plugs"), "Cosmos.Hardware.Plugs.dll"),
|
|
Path.Combine(Path.Combine(ToolsPath, "Cosmos.Sys.Plugs"), "Cosmos.Sys.Plugs.dll")
|
|
};
|
|
}
|
|
else
|
|
{
|
|
string xPath = Path.GetDirectoryName(typeof(Builder).Assembly.Location);
|
|
xPlugs = new string[] {
|
|
Path.Combine(xPath, "Cosmos.Kernel.Plugs.dll"),
|
|
Path.Combine(xPath, "Cosmos.Hardware.Plugs.dll"),
|
|
Path.Combine(xPath, "Cosmos.Sys.Plugs.dll")
|
|
};
|
|
}
|
|
return xPlugs;
|
|
}
|
|
|
|
public void BeginCompile(DebugMode aDebugMode, byte aDebugComport, bool aGDB) {
|
|
if (!Directory.Exists(AsmPath)) {
|
|
Directory.CreateDirectory(AsmPath);
|
|
}
|
|
string[] xPlugs = GetPlugs();
|
|
var xEngineParams = new PassedEngineValue(TargetAssembly.Location, TargetPlatformEnum.X86
|
|
, xPlugs, aDebugMode, aDebugComport, aGDB, AsmPath, Options.TraceAssemblies);
|
|
|
|
if (Options.dotNETFrameworkImplementation == dotNETFrameworkImplementationEnum.Microsoft) {
|
|
var xThread = new Thread(new ParameterizedThreadStart(ThreadExecute));
|
|
xThread.Start(xEngineParams);
|
|
} else {
|
|
var xThread = new Thread(MonoThreadExecute);
|
|
xThread.Start(xEngineParams);
|
|
}
|
|
}
|
|
|
|
public void Assemble() {
|
|
RemoveFile(BuildPath + "output.obj");
|
|
Global.Call(ToolsPath + @"nasm\nasm.exe", String.Format("-g -f bin -o \"{0}\" \"{1}\"", BuildPath + "output.obj", AsmPath + "main.asm"), BuildPath);
|
|
}
|
|
|
|
public void Link() {
|
|
RemoveFile(BuildPath + "output.bin");
|
|
//Global.Call(ToolsPath + @"cygwin\ld.exe", String.Format("-Ttext 0x500000 -Tdata 0x200000 -e Kernel_Start -o \"{0}\" \"{1}\"", "output.bin", "output.obj"), BuildPath);
|
|
File.Move(Path.Combine(BuildPath, "output.obj"), Path.Combine(BuildPath, "output.bin"));
|
|
RemoveFile(BuildPath + "output.obj");
|
|
}
|
|
|
|
public void MakeVPC() {
|
|
MakeISO();
|
|
string xPath = BuildPath + @"VPC\";
|
|
RemoveReadOnlyAttribute(xPath + "Cosmos.vmc");
|
|
RemoveReadOnlyAttribute(xPath + "hda.vhd");
|
|
Process.Start(xPath + "Cosmos.vmc");
|
|
}
|
|
|
|
public void MakeVMWare(bool useVMWareServer) {
|
|
MakeISO();
|
|
string xPath = BuildPath + @"VMWare\";
|
|
|
|
if (useVMWareServer) {
|
|
xPath += @"Server\";
|
|
} else {
|
|
xPath += @"Workstation\";
|
|
}
|
|
|
|
RemoveReadOnlyAttribute(xPath + "Cosmos.nvram");
|
|
RemoveReadOnlyAttribute(xPath + "Cosmos.vmsd");
|
|
RemoveReadOnlyAttribute(xPath + "Cosmos.vmx");
|
|
RemoveReadOnlyAttribute(xPath + "Cosmos.vmxf");
|
|
RemoveReadOnlyAttribute(xPath + "hda.vmdk");
|
|
|
|
Process.Start(xPath + @"Cosmos.vmx");
|
|
}
|
|
|
|
public Process MakeQEMU(bool aUseHDImage, bool aGDB, bool aDebugger, string aDebugComMode, bool aUseNetworkTap, object aNetworkCard, object aAudioCard) {
|
|
MakeISO();
|
|
|
|
//From v0.9.1 Qemu requires forward slashes in path
|
|
BuildPath = BuildPath.Replace('\\', '/');
|
|
|
|
RemoveFile(BuildPath + "serial-debug.txt");
|
|
// QEMU Docs - http://fabrice.bellard.free.fr/qemu/qemu-doc.html
|
|
if (File.Exists(BuildPath + "COM1-output.dbg")) {
|
|
File.Delete(BuildPath + "COM1-output.dbg");
|
|
}
|
|
if (File.Exists(BuildPath + "COM2-output.dbg")) {
|
|
File.Delete(BuildPath + "COM2-output.dbg");
|
|
}
|
|
string xHDString = "";
|
|
if (aUseHDImage) {
|
|
if (File.Exists(BuildPath + "hda.img")) {
|
|
xHDString += "-hda \"" + BuildPath + "hda.img\" ";
|
|
}
|
|
if (File.Exists(BuildPath + "hdb.img")) {
|
|
xHDString += "-hdb \"" + BuildPath + "hdb.img\" ";
|
|
}
|
|
if (File.Exists(BuildPath + "hdd.img")) {
|
|
xHDString += "-hdb \"" + BuildPath + "hdd.img\" ";
|
|
}
|
|
}
|
|
|
|
var xProcess = Global.Call(ToolsPath + @"qemu\qemu.exe"
|
|
// HD image
|
|
, xHDString
|
|
// Path for BIOS, VGA BIOS, and keymaps
|
|
+ " -L ."
|
|
// CD ROM image
|
|
+ " -cdrom \"" + BuildPath + "Cosmos.iso\""
|
|
// Boot CD ROM
|
|
+ " -boot d"
|
|
// Audio hardware
|
|
+ (String.IsNullOrEmpty(aAudioCard as String) ? "" : " -soundhw " + aAudioCard)
|
|
// Setup serial port
|
|
// Might allow serial file later for post debugging of CPU
|
|
// etc since serial to TCP on a byte level is likely highly innefficient
|
|
// with the packet overhead
|
|
//
|
|
// COM1
|
|
+ (aDebugger ? " " + aDebugComMode : " -serial null")
|
|
// COM2
|
|
+ " -serial file:\"" + BuildPath + "COM2-output.dbg\""
|
|
// Enable acceleration if we are not using GDB
|
|
+ (aGDB ? " -S -s" : " -kernel-kqemu")
|
|
// Ethernet card
|
|
+ (String.IsNullOrEmpty(aNetworkCard as String) ? "" : string.Format(" -net nic,model={0},macaddr=52:54:00:12:34:57", aNetworkCard))
|
|
//+ " -redir tcp:5555::23" //use f.instance 'telnet localhost 5555' or 'http://localhost:5555/' to access machine
|
|
+ (String.IsNullOrEmpty(aNetworkCard as String) ? "" : (aUseNetworkTap ? " -net tap,ifname=CosmosTAP" : " -net user")) //requires TAP installed on development computer
|
|
, ToolsPath + @"qemu", false, true);
|
|
|
|
if (aGDB) {
|
|
//TODO: If the host is really busy, sometimes GDB can run before QEMU finishes loading.
|
|
//in this case, GDB says "program not running". Not sure how to fix this properly.
|
|
// Add a sleep? :)
|
|
Global.Call(ToolsPath + "gdb.exe"
|
|
, BuildPath + @"output.bin" + " --eval-command=\"target remote:1234\" --eval-command=\"b _CODE_REQUESTED_BREAK_\" --eval-command=\"c\""
|
|
, ToolsPath + @"qemu\", false, false);
|
|
}
|
|
return xProcess;
|
|
}
|
|
|
|
public void MakeUSB(char aDrive) {
|
|
string xPath = BuildPath + @"USB\";
|
|
RemoveFile(xPath + @"output.bin");
|
|
File.Move(BuildPath + @"output.bin", xPath + @"output.bin");
|
|
// Copy to USB device
|
|
RemoveFile(aDrive + @":\output.bin");
|
|
File.Copy(xPath + @"output.bin", aDrive + @":\output.bin");
|
|
RemoveFile(aDrive + @":\mboot.c32");
|
|
File.Copy(xPath + @"mboot.c32", aDrive + @":\mboot.c32");
|
|
RemoveFile(aDrive + @":\syslinux.cfg");
|
|
File.Copy(xPath + @"syslinux.cfg", aDrive + @":\syslinux.cfg");
|
|
// Set MBR
|
|
//TODO: Hangs on 2008 - maybe needs admin permissions? Or maybe its not compat?
|
|
// Runs from command line ok in 2008.....
|
|
Global.Call(ToolsPath + "syslinux.exe", "-fma " + aDrive + ":", ToolsPath, true, true);
|
|
}
|
|
|
|
public void MakePXE() {
|
|
string xPath = BuildPath + @"PXE\";
|
|
RemoveFile(xPath + @"Boot\output.bin");
|
|
File.Move(BuildPath + "output.bin", xPath + @"Boot\output.bin");
|
|
// *Must* set working dir so tftpd32 will set itself to proper dir
|
|
Global.Call(xPath + "tftpd32.exe", "", xPath, false, true);
|
|
}
|
|
|
|
public void MakeVHD()
|
|
{
|
|
Environment.SetEnvironmentVariable("CosmosBuildPath", BuildPath);
|
|
Global.Call(Environment.GetEnvironmentVariable("WinDir")+ "\\system32\\diskpart /s "+BuildPath+"diskpart_script", "", BuildPath,true,false);
|
|
Environment.SetEnvironmentVariable("CosmosBuildPath","");
|
|
}
|
|
}
|
|
}
|