原文地址:http://www.codeproject.com/KB/cs/CosmosPlugs.aspx安全
介紹網絡
這篇文章將展現在COSMOS中如何實現基於WINDOWS API調用和內部調用的.net代碼。另外,也包含了如何使用COSMOS,彙編或者X#語言與硬件直接進行交互。框架
Cosmos是什麼ide
Cosmos一個使用Visual Studio做爲開發環境的操做系統開發工具(development kit)。儘管如此,任何基於.net的語言,包括VB.NET,Fortran,Delphi Prism,IronPython,F#等等均可以用來進行開發。Cosmos本省和內核運行都是使用C# 來寫的,因此命名爲Cosmos。並且,NOSMOS(.NET Open Source Managed Operating System)聽起來太蠢了。工具
Cosmos不是傳統意義上的操做系統,它更應該叫作操做系統工具(Operating System Kit) ,或者正如我所說的叫作「Operating System Legos」(不知如何翻譯,嘿嘿)。Cosmos讓您能像使用Visual Studio和C#建立應用程序同樣建立操做系統。大部分用戶能夠在幾分鐘以內本身寫和引導一個他們本身的操做系統,全部這些均可以在Visual Studio中完成。Cosmos提供了與Visual Studio集成的項目類型、調試器、斷點工具和觀察其(watchers)等。你能夠向調試你的C#或者VB.NET應用程序同樣調試你的操做系統。開發工具
什麼狀況下須要Plugs優化
在如下三種場景中須要用到Plugs:this
1. 內部調用(Internal Calls)spa
2. P/Invoke操作系統
3. 直接彙編
內部調用和P/Invoke的狀況
在.net框架提供的各類類中,有一部分不是使用.net代碼來實現的,而是使用的本地代碼。這樣作的緣由有兩:
1. 被實現的方法依賴於Windows API(P/Invoke的狀況)
2.被實現的方法依賴於高度優化過的C++或者在.net運行時中的彙編代碼(內部調用的狀況)
P/Invoke被用來往屏幕上畫圖,訪問已存在的Window encrypition API,訪問網絡和其餘一些相似的功能。
內部調用被須要直接訪問.net運行時的類來使用。好比直接訪問內存管理的類,或者對速度有要求的狀況。如Math.Pow方法就是使用的內部調用。
Plugs可使用C# ,彙編或者任何.net語言來實現。
直接彙編的狀況
對於直接與硬件進行會話,Cosmos必須可以和PIC總線,CPU IO總線,內存等進行交互。訪問內存常用的是非類型安全的指針(unsafe pointers),儘管如此,在某些狀況下仍是得本身手寫彙編代碼。Plugs至關於接口,提供給C#直接訪問彙編代碼,使得對彙編調用的訪問就像C#代碼的方法調用同樣。
在Cosmos中寫X86彙編
能夠在Cosmos中使用類來實現X86彙編
View Codenew Move(Registers.DX, (xComAddr + 1 ).ToString()); new Move(Registers.AL, .ToString()); new Out( " dx " , " al " ); // disable all interrupts new Move(Registers.DX, (xComAddr + 3 ).ToString()); new Move(Registers.AL, 0x80 .ToString()); new Out( " dx " , " al " ); // Enable DLAB (set baud rate divisor) new Move(Registers.DX, (xComAddr + ).ToString()); new Move(Registers.AL, 0x1 .ToString()); new Out( " dx " , " al " ); // Set diviso (low byte) new Move(Registers.DX, (xComAddr + 1 ).ToString()); new Move(Registers.AL, 0x00 .ToString()); new Out( " dx " , " al " ); // // set divisor (high byte)
可是 Cosmos也支持一個更高層次的調用X#。X#是一種類型安全的直接與X86彙編對應的彙編語言。X#以下:
UInt16 xComStatusAddr = (UInt16)(aComAddr + 5 ); Label = " WriteByteToComPort " ; Label = " WriteByteToComPort_Wait " ; DX = xComStatusAddr; AL = Port[DX]; AL.Test( 0x20 ); JumpIfEqual( " WriteByteToComPort_Wait " ); DX = aComAddr; AL = Memory[ESP + 4 ]; Port[DX] = AL; Return( 4 ); Label = " DebugWriteEIP " ; AL = Memory[EBP + 3 ]; EAX.Push(); Call < WriteByteToComPort > (); AL = Memory[EBP + 2 ]; EAX.Push(); Call < WriteByteToComPort > (); AL = Memory[EBP + 1 ]; EAX.Push(); Call < WriteByteToComPort > (); AL = Memory[EBP]; EAX.Push(); Call < WriteByteToComPort > (); Return();
開始實現Plugs
首先咱們必須先決定咱們的plug要幹什麼。舉例來講,Math.Abs(double)被用來實現一個內部調用
.method public hidebysig static float64 Abs(float64 ' value ' ) cil managed internalcall { .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() }
若是你直接使用這個方法而Cosmos沒有相應的plug,編譯器便會產生一個「plug needed」錯誤。由於在IL2CPU沒有IL代碼把它編譯爲X86代碼。因此「plug needed」錯誤的意思是你須要一些依賴內部調用或者P/Invoke的方法,不然Cosmos將沒法編譯。
在Math.Pow這個例子中,是能夠經過編譯的由於Cosmos的內核已經包含了一個在編譯的時候會自動被調用的plug.
編譯器在運行時中使用plug來替換實際的代碼。Plug中提供的代碼被用來替換對內部調用和WINDOWS API的調用,這些調用沒法在Cosmos中直接使用由於Cosmos不是運行在Windows或者CLR下面的。Plug就是這麼一個強制嵌入和替換的格式(It’s a form of forced inlining and replacement)
爲了建立一個Plug,咱們須要建立一個新的類。在內核中的Plug被建立爲各個單獨的程序集(assemblies)而且被內核單獨引用。這樣容許IL2CPU包含和使用plug。
[Plug(Target = typeof ( global ::System.Math))] public class MathImpl { public static double Abs( double value) { if (value < ) { return - value; } else { return value; } }
雖然這裏是顯示了一個方法,但其實Plug類能包括多個方法。在這個例子中Plug的屬性(attribute)是關鍵因素。它告訴IL2CPU這個plug用來替換System.Math類中的方法。而後IL2CPU便會去找與System.Math中方法對應的方法,並把他們給換掉。
直接彙編的plug
直接彙編的plug是被用來運行C#直接和X86彙編進行交互的代碼。好比IOPort類容許設備驅動程序在須要和硬件設備通訊的時候直接訪問CPU總線。
首先建立一個空的C# 類,建立將要被替換的空方法。若是這個被替換的方法的返回類型不是VOID,則plug中的方法須要隨便返回一個值以使C# 編譯器能編譯它。儘管如此這個方法的返回值是不會被用到的,由於plug將使被替換的目標方法被忽略,而以plug中實現的方法替換掉原方法。
public abstract class IOPortBase { public readonly UInt16 Port; // all ctors are internal - Only Core ring can create it.. but hardware ring can use it. internal IOPortBase(UInt16 aPort) { Port = aPort; } internal IOPortBase(UInt16 aBase, UInt16 aOffset) { // C# math promotes things to integers, so we have this constructor // to relieve the use from having to do so many casts Port = (UInt16)(aBase + aOffset); } // TODO: Reads and writes can use this to get port instead of argument static protected void Write8(UInt16 aPort, byte aData) { } // Plugged static protected void Write16(UInt16 aPort, UInt16 aData) { } // Plugged static protected void Write32(UInt16 aPort, UInt32 aData) { } // Plugged static protected byte Read8(UInt16 aPort) { return ; } // Plugged static protected UInt16 Read16(UInt16 aPort) { return ; } // Plugged static protected UInt32 Read32(UInt16 aPort) { return ; } // Plugged
正如你看到的「Write」方法是空的,而「Read」方法須要 一個名義上的返回值。
這個類將被一下代碼給替換掉:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Cosmos.IL2CPU.Plugs; using Assembler = Cosmos.Compiler.Assembler.Assembler; using CPUx86 = Cosmos.Compiler.Assembler.X86; namespace Cosmos.Core.Plugs { [Plug(Target = typeof (Cosmos.Core.IOPortBase))] public class IOPortImpl { [Inline] public static void Write8(UInt16 aPort, byte aData) { // TODO: This is a lot of work to write to a single port. // We need to have some kind of inline ASM option that can // emit a single out instruction new CPUx86.Move { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EBP, SourceDisplacement = 0x0C , SourceIsIndirect = true }; new CPUx86.Move { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.EBP, SourceDisplacement = 0x08 , SourceIsIndirect = true }; new CPUx86.Out { DestinationReg = CPUx86.Registers.AL }; } [Inline] public static void Write16(UInt16 aPort, UInt16 aData) { new CPUx86.Move { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x0C }; new CPUx86.Move { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x08 }; new CPUx86.Out { DestinationReg = CPUx86.Registers.AX }; } [Inline] public static void Write32(UInt16 aPort, UInt32 aData) { new CPUx86.Move { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x0C }; new CPUx86.Move { DestinationReg = CPUx86.Registers.EAX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x08 }; new CPUx86.Out { DestinationReg = CPUx86.Registers.EAX }; } [Inline] public static byte Read8(UInt16 aPort) { new CPUx86.Move { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x08 }; // TODO: Do we need to clear rest of EAX first? // MTW: technically not, as in other places, it _should_ be working with AL too.. new CPUx86.Move { DestinationReg = CPUx86.Registers.EAX, SourceValue = }; new CPUx86.In { DestinationReg = CPUx86.Registers.AL }; new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; return ; } [Inline] public static UInt16 Read16(UInt16 aPort) { new CPUx86.Move { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x08 }; new CPUx86.Move { DestinationReg = CPUx86.Registers.EAX, SourceValue = }; new CPUx86.In { DestinationReg = CPUx86.Registers.AX }; new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; return ; } [Inline] public static UInt32 Read32(UInt16 aPort) { new CPUx86.Move { DestinationReg = CPUx86.Registers.EDX, SourceReg = CPUx86.Registers.EBP, SourceIsIndirect = true , SourceDisplacement = 0x08 }; new CPUx86.In { DestinationReg = CPUx86.Registers.EAX }; new CPUx86.Push { DestinationReg = CPUx86.Registers.EAX }; return ; } } }
注:在這個例子中的代碼(指那些看起來像彙編的代碼—譯者注)不是X#代碼。咱們一些比較老的plug任然是使用比較老的語法來寫的。
如今咱們有了一個plug,咱們可使用C#直接訪問IOPort這個類。下面這個例子摘自ATA類
public override void ReadBlock(UInt64 aBlockNo, UInt32 aBlockCount, byte [] aData) { CheckDataSize(aData, aBlockCount); SelectSector(aBlockNo, aBlockCount); SendCmd(Cmd.ReadPio); IO.Data.Read8(aData); }
其餘plug的例子
在BCL(Binary Classes Library?。.net中框架提供的類),Console類中使用的一些內部調用一般最後都使用到了WINDOWS API,咱們不須要逐個替換每個調用層次上直接映射到WINDOWS API調用,而只需在一個更高的調用層次樹上調用咱們的TextScreen類徹底替換這些方法。(We don't need to plug only the methods that directly map to Windows API calls, but instead we plug methods much higher up the tree and completely replace the implementation to call our TextScreen class instead.)
namespace Cosmos.System.Plugs.System.System { [Plug(Target = typeof ( global ::System.Console))] public static class ConsoleImpl { private static ConsoleColor mForeground = ConsoleColor.White; private static ConsoleColor mBackground = ConsoleColor.Black; public static ConsoleColor get_BackgroundColor() { return mBackground; } public static void set_BackgroundColor(ConsoleColor value) { mBackground = value; Cosmos.Hardware.Global.TextScreen.SetColors(mForeground, mBackground); }
(翻譯就到此結束了,水平有限,歡迎你們指點)