using System.Runtime.InteropServices;
namespace XFESystem.NativeKernel;
public unsafe struct UsbMassDevice
{
public UsbDevice* Device;
public UsbRing InputRing, OutputRing;
public ulong Packet, Data, Completed, Blocks, PartitionStart, PartitionEnd, AllowedStart, AllowedCount;
public uint InputDci, OutputDci, CompletionCode, Remaining, Active, Tag, BootMatch;
}
// USB-IF Bulk-Only Transport, SCSI transparent command set, LUN 0, 512-byte sectors.
// No native buffer passed here belongs to the managed GC. Only the USB worker
// owns rings/DMA; callers exchange bounded copies through a kernel mailbox.
public static unsafe partial class UsbXhci
{
static UsbMassDevice* _mass, _bootMass;
static uint _massCount, _bootMatches, _requestState, _requestKind, _requestCount, _requestDevice;
static ulong _requestLba, _mailbox, _writeStart, _writeCount, _storageReads, _storageWrites, _storageFlushes;
static uint Crc(byte* p,uint count)
{ uint crc=0xffffffff;for(uint i=0;i<count;i++){crc^=p[i];for(uint j=0;j<8;j++)crc=crc>>1^((crc&1)!=0?0xedb88320u:0);}return ~crc; }
static uint Be32(byte* p) => (uint)p[0]<<24|(uint)p[1]<<16|(uint)p[2]<<8|p[3];
static void SetBe32(byte* p,uint n){p[0]=(byte)(n>>24);p[1]=(byte)(n>>16);p[2]=(byte)(n>>8);p[3]=(byte)n;}
static void AddStorage(UsbDevice* d,byte* descriptors,uint bytes)
{
uint iface=0,valid=0,input=0,output=0,inPacket=0,outPacket=0;
for(uint at=0;at+2<=bytes;)
{
uint size=descriptors[at],type=descriptors[at+1];if(size<2||size>bytes-at)break;
if(type==4&&size>=9)
{
if(valid!=0&&input!=0&&output!=0)break;
iface=descriptors[at+2];input=output=0;
valid=descriptors[at+3]==0&&descriptors[at+5]==8&&descriptors[at+6]==6&&descriptors[at+7]==80?1u:0;
}
else if(type==5&&size>=7&&valid!=0&&(descriptors[at+3]&3)==2)
{
uint address=descriptors[at+2],packet=(uint)(descriptors[at+4]|descriptors[at+5]<<8)&2047;
if((address&15)!=0&&packet!=0&&packet<=1024)
{if((address&128)!=0){input=address;inPacket=packet;}else{output=address;outPacket=packet;}}
}
at+=size;
}
if(valid==0||input==0||output==0||_massCount>=8)return;
if(_mass==null)_mass=(UsbMassDevice*)At(Allocate(2));
if(_mass==null)return;
UsbMassDevice* m=&_mass[_massCount++];m->Device=d;m->InputDci=(input&15)*2+1;m->OutputDci=(output&15)*2;
m->Packet=Allocate();ulong storage=Allocate(32);m->Data=(storage+65535)&~65535UL;
if(m->Packet==0||storage==0||!NewRing(&m->InputRing)||!NewRing(&m->OutputRing))return;
uint* ic=Context(d,0);ic[0]=0;ic[1]=1u|(1u<<(int)m->InputDci)|(1u<<(int)m->OutputDci);CopyContext(d,1);
uint last=m->InputDci>m->OutputDci?m->InputDci:m->OutputDci;
uint* sc=Context(d,1);if((sc[0]>>27)<last)sc[0]=(sc[0]&0x7ffffff)|last<<27;
uint* ei=Context(d,m->InputDci+1),eo=Context(d,m->OutputDci+1);
ei[1]=3u<<1|6u<<3|inPacket<<16;((ulong*)(ei+2))[0]=m->InputRing.Physical|1;ei[4]=inPacket;
eo[1]=3u<<1|2u<<3|outPacket<<16;((ulong*)(eo+2))[0]=m->OutputRing.Physical|1;eo[4]=outPacket;
uint slot;if(!Command(d->Controller,12,d->Input,d->Slot<<24,out slot)){DeviceLog(d,"STORAGE_ENDPOINT_FAILED");return;}
m->Active=1;
byte* cdb=stackalloc byte[16];for(uint i=0;i<16;i++)cdb[i]=0;
bool ready=false;
for(uint attempt=0;attempt<8;attempt++)
{
cdb[0]=0;if(Scsi(m,cdb,6,0,true)){ready=true;break;}
cdb[0]=3;cdb[4]=18;Scsi(m,cdb,6,18,true);cdb[4]=0;Sleep(100);
}
if(!ready){m->Active=0;DeviceLog(d,"STORAGE_NOT_READY");return;}
cdb[0]=0x25;if(!Scsi(m,cdb,10,8,true)){m->Active=0;return;}
byte* capacity=At(m->Data);uint lastBlock=Be32(capacity),sectorBytes=Be32(capacity+4);
if(lastBlock==0xffffffff||sectorBytes!=512){m->Active=0;DeviceLog(d,"STORAGE_SECTOR_SIZE_OR_CAPACITY_UNSUPPORTED");return;}
m->Blocks=(ulong)lastBlock+1;
if(_mailbox==0)_mailbox=Allocate(16);
if(_mailbox==0){m->Active=0;return;}
if(!MatchBootVolume(m)){DeviceLog(d,"STORAGE_NOT_BOOT_VOLUME_READ_ONLY");return;}
m->BootMatch=1;_bootMatches++;_bootMass=m;
// One short cold-start record, before the boot workspace is available.
// Runtime/disk worker preemption must not splice fields into this ABI
// acknowledgement. UART polling remains bounded; no per-I/O or frame
// logging is added to the interrupt-excluded region.
ulong readyLogFlags=Native.IrqSave();
Serial.Write("XFE_USB_STORAGE_READY|Matches|");Serial.Number(_bootMatches);Serial.Write("|Blocks|");Serial.Number(m->Blocks);Serial.Line("|SectorBytes|512|Transport|BOT");
Native.IrqRestore(readyLogFlags);
}
static bool CompleteStorage(UsbDevice* d,uint endpoint,ulong pointer,uint code,uint remaining)
{
for(uint i=0;i<_massCount;i++)
{
UsbMassDevice* m=&_mass[i];if(m->Device!=d||(endpoint!=m->InputDci&&endpoint!=m->OutputDci))continue;
m->Completed=pointer;m->CompletionCode=code;m->Remaining=remaining;return true;
}
return false;
}
static bool Bulk(UsbMassDevice* m,bool input,ulong address,uint bytes)
{
if(m->Active==0||m->Device->Connected==0||bytes==0||bytes>65536||(address&65535)+bytes>65536)return false;
UsbRing* ring=input?&m->InputRing:&m->OutputRing;
ulong pending=Put(ring,address,bytes,(1u<<10)|32|4);m->Completed=0;
Write(m->Device->Controller->Doorbell+m->Device->Slot*4,input?m->InputDci:m->OutputDci);
ulong end=Now()+2000;
do
{
Events(m->Device->Controller);
if(m->Completed==pending)
{
if((m->CompletionCode==1||m->CompletionCode==13)&&m->Remaining==0)return true;
break;
}
Sleep(1);
}while(Now()<end);
m->Active=0;Failures++;DeviceLog(m->Device,"STORAGE_TRANSFER_FAILED_QUARANTINED");return false;
}
static bool Scsi(UsbMassDevice* m,byte* command,uint commandBytes,uint bytes,bool input)
{
if(commandBytes==0||commandBytes>16||bytes>65536||m->Active==0)return false;
byte* packet=At(m->Packet);for(uint i=0;i<31;i++)packet[i]=0;
*(uint*)packet=0x43425355;*(uint*)(packet+4)=++m->Tag;*(uint*)(packet+8)=bytes;
packet[12]=input?(byte)128:(byte)0;packet[14]=(byte)commandBytes;
for(uint i=0;i<commandBytes;i++)packet[15+i]=command[i];
if(!Bulk(m,false,m->Packet,31)||bytes!=0&&!Bulk(m,input,m->Data,bytes)||!Bulk(m,true,m->Packet,13))return false;
if(*(uint*)packet!=0x53425355||*(uint*)(packet+4)!=m->Tag||*(uint*)(packet+8)!=0||packet[12]>1)
{m->Active=0;Failures++;return false;}
return packet[12]==0;
}
static bool Sectors(UsbMassDevice* m,ulong lba,uint count,bool write)
{
if(m==null||count==0||count>128||lba>=m->Blocks||count>m->Blocks-lba||lba>0xffffffff)return false;
if(write)
{
ulong start=m==_bootMass?_writeStart:m->AllowedStart,size=m==_bootMass?_writeCount:m->AllowedCount;
if(m==_bootMass?_bootMatches!=1:m->BootMatch!=0)return false;
if(size==0||lba<start||lba-start>=size||count>size-(lba-start))return false;
}
byte* command=stackalloc byte[16];for(uint i=0;i<16;i++)command[i]=0;
command[0]=write?(byte)0x2a:(byte)0x28;SetBe32(command+2,(uint)lba);command[7]=(byte)(count>>8);command[8]=(byte)count;
bool ok=Scsi(m,command,10,count*512,!write);
if(ok){if(write)_storageWrites+=count;else _storageReads+=count;}return ok;
}
static bool MatchBootVolume(UsbMassDevice* m)
{
NativeBootInfo* boot=Kernel.Boot;
if((boot->BootDiskIdLow|boot->BootDiskIdHigh)==0||(boot->BootPartitionIdLow|boot->BootPartitionIdHigh)==0||!Sectors(m,1,1,false))return false;
byte* header=At(m->Data);
if(*(ulong*)header!=0x5452415020494645UL||*(uint*)(header+12)!=92||((ulong*)(header+56))[0]!=boot->BootDiskIdLow||((ulong*)(header+56))[1]!=boot->BootDiskIdHigh)return false;
byte* copy=stackalloc byte[92];for(uint i=0;i<92;i++)copy[i]=header[i];*(uint*)(copy+16)=0;
if(Crc(copy,92)!=*(uint*)(header+16)||*(uint*)(header+80)!=128||*(uint*)(header+84)!=128)return false;
ulong entries=*(ulong*)(header+72);uint crc=*(uint*)(header+88);
if(!Sectors(m,entries,32,false)||Crc(At(m->Data),16384)!=crc)return false;
byte* table=At(m->Data);
for(uint i=0;i<128;i++)
{
byte* entry=table+i*128;
if(((ulong*)(entry+16))[0]!=boot->BootPartitionIdLow||((ulong*)(entry+16))[1]!=boot->BootPartitionIdHigh)continue;
m->PartitionStart=*(ulong*)(entry+32);m->PartitionEnd=*(ulong*)(entry+40);
return m->PartitionStart>=34&&m->PartitionEnd>=m->PartitionStart&&m->PartitionEnd<m->Blocks;
}
return false;
}
static void DisconnectStorage(UsbDevice* d)
{
for(uint i=0;i<_massCount;i++)if(_mass[i].Device==d){_mass[i].Active=0;if(_mass[i].BootMatch!=0){_mass[i].BootMatch=0;if(_bootMatches!=0)_bootMatches--;}}
if(_bootMass!=null&&_bootMass->Device==d){_writeCount=0;_bootMass=null;}
}
static void ProcessStorageRequest()
{
Native.MemoryBarrier();if(_requestState!=1)return;_requestState=2;
bool ok=false;UsbMassDevice* m=_requestDevice==0xffffffffu?_bootMass:_requestDevice<_massCount?&_mass[_requestDevice]:null;
if(m!=null&&m->Active!=0&&m->Device->Connected!=0&&(_requestDevice==0xffffffffu?_bootMatches==1:m->BootMatch==0))
{
if(_requestKind==3)
{
byte* command=stackalloc byte[16];for(uint i=0;i<16;i++)command[i]=0;command[0]=0x35;
ok=Scsi(m,command,10,0,true);if(ok)_storageFlushes++;
}
else
{
if(_requestKind==2)for(uint i=0;i<_requestCount*512;i++)At(m->Data)[i]=At(_mailbox)[i];
ok=Sectors(m,_requestLba,_requestCount,_requestKind==2);
if(ok&&_requestKind==1)for(uint i=0;i<_requestCount*512;i++)At(_mailbox)[i]=At(m->Data)[i];
}
}
Native.MemoryBarrier();_requestState=_requestState==6?0u:ok?3u:4u;
}
static int Request(uint kind,ulong lba,uint count,byte* buffer,uint device=0xffffffffu)
{
UsbMassDevice* m=device==0xffffffffu?_bootMass:device<_massCount?&_mass[device]:null;
if(m==null||m->Active==0||m->Device->Connected==0||(device==0xffffffffu?_bootMatches!=1:m->BootMatch!=0)||_mailbox==0||kind!=3&&(buffer==null||count==0||count>128))return 0;
ulong irq=Native.IrqSave();if(_requestState!=0){Native.IrqRestore(irq);return 0;}
if(kind==2)for(uint i=0;i<count*512;i++)At(_mailbox)[i]=buffer[i];
_requestDevice=device;_requestKind=kind;_requestLba=lba;_requestCount=count;_requestState=1;Native.MemoryBarrier();Native.IrqRestore(irq);
ulong end=Now()+10000;
for(;;)
{
Native.MemoryBarrier();uint state=_requestState;
if(state==3||state==4)
{
if(state==3&&kind==1)for(uint i=0;i<count*512;i++)buffer[i]=At(_mailbox)[i];
_requestState=0;Native.MemoryBarrier();return state==3?1:0;
}
if(Now()>=end)
{irq=Native.IrqSave();uint current=_requestState;if(current==1)_requestState=0;else if(current==2)_requestState=6;Native.IrqRestore(irq);if(current==3||current==4)continue;return 0;}
Sleep(1);
}
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_info")]
public static ulong StorageInfo(uint field)
{
Native.MemoryBarrier();UsbMassDevice* m=_bootMatches==1?_bootMass:null;
return field switch {0=>1,1=>State,2=>m!=null&&m->Active!=0?1UL:0,3=>m==null?0:m->Blocks,4=>512,
5=>m==null?0:m->PartitionStart,6=>m==null?0:m->PartitionEnd,7=>_storageReads,8=>_storageWrites,9=>_storageFlushes,
10=>_writeStart,11=>_writeCount,12=>Kernel.Boot->BootDiskIdLow,13=>Kernel.Boot->BootDiskIdHigh,
14=>Kernel.Boot->BootPartitionIdLow,15=>Kernel.Boot->BootPartitionIdHigh,_=>0};
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_read")]
public static int StorageRead(ulong lba,uint count,byte* buffer)=>Request(1,lba,count,buffer);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_write")]
public static int StorageWrite(ulong lba,uint count,byte* buffer)=>Request(2,lba,count,buffer);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_flush")]
public static int StorageFlush()=>Request(3,0,0,null);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_allow_workspace")]
public static int AllowWorkspace(ulong lba,uint count)
{
if(_bootMatches!=1||_bootMass==null||_writeCount!=0||count!=131072||lba<_bootMass->PartitionStart||lba>_bootMass->PartitionEnd||count>_bootMass->PartitionEnd-lba+1)return 0;
byte* descriptor=stackalloc byte[512];if(Request(1,lba,1,descriptor)!=1)return 0;
if(*(ulong*)descriptor!=0x5357425355454658UL||*(uint*)(descriptor+8)!=1||*(uint*)(descriptor+12)!=count||
*(uint*)(descriptor+16)!=65535||*(uint*)(descriptor+20)!=Crc(descriptor,20))return 0;
_writeStart=lba+1;_writeCount=131070;Native.MemoryBarrier();return 1;
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_info")]
public static ulong OtherInfo(uint index,uint field)
{
Native.MemoryBarrier();if(field==0)return State;if(field==1)return _massCount;if(index>=_massCount)return 0;
UsbMassDevice* m=&_mass[index];return field switch {2=>m->Active!=0&&m->Device->Connected!=0&&m->BootMatch==0?1UL:0,3=>m->Blocks,_=>0};
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_read")]
public static int OtherRead(uint index,ulong lba,uint count,byte* buffer)=>Request(1,lba,count,buffer,index);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_write")]
public static int OtherWrite(uint index,ulong lba,uint count,byte* buffer)=>Request(2,lba,count,buffer,index);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_flush")]
public static int OtherFlush(uint index)=>Request(3,0,0,null,index);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_allow_partition")]
public static int OtherAllow(uint index,ulong start,ulong count)
{
if(index>=_massCount||_mass[index].Active==0||_mass[index].BootMatch!=0||start==0||count==0||start>=_mass[index].Blocks||count>_mass[index].Blocks-start)return 0;
ulong irq=Native.IrqSave();if(_requestState!=0){Native.IrqRestore(irq);return 0;}
_mass[index].AllowedStart=start;_mass[index].AllowedCount=count;Native.MemoryBarrier();Native.IrqRestore(irq);return 1;
}
}
using System.Runtime.InteropServices;
namespace XFESystem.NativeKernel;
public unsafe struct UsbMassDevice
{
public UsbDevice* Device;
public UsbRing InputRing, OutputRing;
public ulong Packet, Data, Completed, Blocks, PartitionStart, PartitionEnd, AllowedStart, AllowedCount;
public uint InputDci, OutputDci, CompletionCode, Remaining, Active, Tag, BootMatch;
}
// USB-IF Bulk-Only Transport, SCSI transparent command set, LUN 0, 512-byte sectors.
// No native buffer passed here belongs to the managed GC. Only the USB worker
// owns rings/DMA; callers exchange bounded copies through a kernel mailbox.
public static unsafe partial class UsbXhci
{
static UsbMassDevice* _mass, _bootMass;
static uint _massCount, _bootMatches, _requestState, _requestKind, _requestCount, _requestDevice;
static ulong _requestLba, _mailbox, _writeStart, _writeCount, _storageReads, _storageWrites, _storageFlushes;
static uint Crc(byte* p,uint count)
{ uint crc=0xffffffff;for(uint i=0;i<count;i++){crc^=p[i];for(uint j=0;j<8;j++)crc=crc>>1^((crc&1)!=0?0xedb88320u:0);}return ~crc; }
static uint Be32(byte* p) => (uint)p[0]<<24|(uint)p[1]<<16|(uint)p[2]<<8|p[3];
static void SetBe32(byte* p,uint n){p[0]=(byte)(n>>24);p[1]=(byte)(n>>16);p[2]=(byte)(n>>8);p[3]=(byte)n;}
static void AddStorage(UsbDevice* d,byte* descriptors,uint bytes)
{
uint iface=0,valid=0,input=0,output=0,inPacket=0,outPacket=0;
for(uint at=0;at+2<=bytes;)
{
uint size=descriptors[at],type=descriptors[at+1];if(size<2||size>bytes-at)break;
if(type==4&&size>=9)
{
if(valid!=0&&input!=0&&output!=0)break;
iface=descriptors[at+2];input=output=0;
valid=descriptors[at+3]==0&&descriptors[at+5]==8&&descriptors[at+6]==6&&descriptors[at+7]==80?1u:0;
}
else if(type==5&&size>=7&&valid!=0&&(descriptors[at+3]&3)==2)
{
uint address=descriptors[at+2],packet=(uint)(descriptors[at+4]|descriptors[at+5]<<8)&2047;
if((address&15)!=0&&packet!=0&&packet<=1024)
{if((address&128)!=0){input=address;inPacket=packet;}else{output=address;outPacket=packet;}}
}
at+=size;
}
if(valid==0||input==0||output==0||_massCount>=8)return;
if(_mass==null)_mass=(UsbMassDevice*)At(Allocate(2));
if(_mass==null)return;
UsbMassDevice* m=&_mass[_massCount++];m->Device=d;m->InputDci=(input&15)*2+1;m->OutputDci=(output&15)*2;
m->Packet=Allocate();ulong storage=Allocate(32);m->Data=(storage+65535)&~65535UL;
if(m->Packet==0||storage==0||!NewRing(&m->InputRing)||!NewRing(&m->OutputRing))return;
uint* ic=Context(d,0);ic[0]=0;ic[1]=1u|(1u<<(int)m->InputDci)|(1u<<(int)m->OutputDci);CopyContext(d,1);
uint last=m->InputDci>m->OutputDci?m->InputDci:m->OutputDci;
uint* sc=Context(d,1);if((sc[0]>>27)<last)sc[0]=(sc[0]&0x7ffffff)|last<<27;
uint* ei=Context(d,m->InputDci+1),eo=Context(d,m->OutputDci+1);
ei[1]=3u<<1|6u<<3|inPacket<<16;((ulong*)(ei+2))[0]=m->InputRing.Physical|1;ei[4]=inPacket;
eo[1]=3u<<1|2u<<3|outPacket<<16;((ulong*)(eo+2))[0]=m->OutputRing.Physical|1;eo[4]=outPacket;
uint slot;if(!Command(d->Controller,12,d->Input,d->Slot<<24,out slot)){DeviceLog(d,"STORAGE_ENDPOINT_FAILED");return;}
m->Active=1;
byte* cdb=stackalloc byte[16];for(uint i=0;i<16;i++)cdb[i]=0;
bool ready=false;
for(uint attempt=0;attempt<8;attempt++)
{
cdb[0]=0;if(Scsi(m,cdb,6,0,true)){ready=true;break;}
cdb[0]=3;cdb[4]=18;Scsi(m,cdb,6,18,true);cdb[4]=0;Sleep(100);
}
if(!ready){m->Active=0;DeviceLog(d,"STORAGE_NOT_READY");return;}
cdb[0]=0x25;if(!Scsi(m,cdb,10,8,true)){m->Active=0;return;}
byte* capacity=At(m->Data);uint lastBlock=Be32(capacity),sectorBytes=Be32(capacity+4);
if(lastBlock==0xffffffff||sectorBytes!=512){m->Active=0;DeviceLog(d,"STORAGE_SECTOR_SIZE_OR_CAPACITY_UNSUPPORTED");return;}
m->Blocks=(ulong)lastBlock+1;
if(_mailbox==0)_mailbox=Allocate(16);
if(_mailbox==0){m->Active=0;return;}
if(!MatchBootVolume(m)){DeviceLog(d,"STORAGE_NOT_BOOT_VOLUME_READ_ONLY");return;}
m->BootMatch=1;_bootMatches++;_bootMass=m;
// One short cold-start record, before the boot workspace is available.
// Runtime/disk worker preemption must not splice fields into this ABI
// acknowledgement. UART polling remains bounded; no per-I/O or frame
// logging is added to the interrupt-excluded region.
ulong readyLogFlags=Native.IrqSave();
Serial.Write("XFE_USB_STORAGE_READY|Matches|");Serial.Number(_bootMatches);Serial.Write("|Blocks|");Serial.Number(m->Blocks);Serial.Line("|SectorBytes|512|Transport|BOT");
Native.IrqRestore(readyLogFlags);
}
static bool CompleteStorage(UsbDevice* d,uint endpoint,ulong pointer,uint code,uint remaining)
{
for(uint i=0;i<_massCount;i++)
{
UsbMassDevice* m=&_mass[i];if(m->Device!=d||(endpoint!=m->InputDci&&endpoint!=m->OutputDci))continue;
m->Completed=pointer;m->CompletionCode=code;m->Remaining=remaining;return true;
}
return false;
}
static bool Bulk(UsbMassDevice* m,bool input,ulong address,uint bytes)
{
if(m->Active==0||m->Device->Connected==0||bytes==0||bytes>65536||(address&65535)+bytes>65536)return false;
UsbRing* ring=input?&m->InputRing:&m->OutputRing;
ulong pending=Put(ring,address,bytes,(1u<<10)|32|4);m->Completed=0;
Write(m->Device->Controller->Doorbell+m->Device->Slot*4,input?m->InputDci:m->OutputDci);
ulong end=Now()+2000;
do
{
Events(m->Device->Controller);
if(m->Completed==pending)
{
if((m->CompletionCode==1||m->CompletionCode==13)&&m->Remaining==0)return true;
break;
}
Sleep(1);
}while(Now()<end);
m->Active=0;Failures++;DeviceLog(m->Device,"STORAGE_TRANSFER_FAILED_QUARANTINED");return false;
}
static bool Scsi(UsbMassDevice* m,byte* command,uint commandBytes,uint bytes,bool input)
{
if(commandBytes==0||commandBytes>16||bytes>65536||m->Active==0)return false;
byte* packet=At(m->Packet);for(uint i=0;i<31;i++)packet[i]=0;
*(uint*)packet=0x43425355;*(uint*)(packet+4)=++m->Tag;*(uint*)(packet+8)=bytes;
packet[12]=input?(byte)128:(byte)0;packet[14]=(byte)commandBytes;
for(uint i=0;i<commandBytes;i++)packet[15+i]=command[i];
if(!Bulk(m,false,m->Packet,31)||bytes!=0&&!Bulk(m,input,m->Data,bytes)||!Bulk(m,true,m->Packet,13))return false;
if(*(uint*)packet!=0x53425355||*(uint*)(packet+4)!=m->Tag||*(uint*)(packet+8)!=0||packet[12]>1)
{m->Active=0;Failures++;return false;}
return packet[12]==0;
}
static bool Sectors(UsbMassDevice* m,ulong lba,uint count,bool write)
{
if(m==null||count==0||count>128||lba>=m->Blocks||count>m->Blocks-lba||lba>0xffffffff)return false;
if(write)
{
ulong start=m==_bootMass?_writeStart:m->AllowedStart,size=m==_bootMass?_writeCount:m->AllowedCount;
if(m==_bootMass?_bootMatches!=1:m->BootMatch!=0)return false;
if(size==0||lba<start||lba-start>=size||count>size-(lba-start))return false;
}
byte* command=stackalloc byte[16];for(uint i=0;i<16;i++)command[i]=0;
command[0]=write?(byte)0x2a:(byte)0x28;SetBe32(command+2,(uint)lba);command[7]=(byte)(count>>8);command[8]=(byte)count;
bool ok=Scsi(m,command,10,count*512,!write);
if(ok){if(write)_storageWrites+=count;else _storageReads+=count;}return ok;
}
static bool MatchBootVolume(UsbMassDevice* m)
{
NativeBootInfo* boot=Kernel.Boot;
if((boot->BootDiskIdLow|boot->BootDiskIdHigh)==0||(boot->BootPartitionIdLow|boot->BootPartitionIdHigh)==0||!Sectors(m,1,1,false))return false;
byte* header=At(m->Data);
if(*(ulong*)header!=0x5452415020494645UL||*(uint*)(header+12)!=92||((ulong*)(header+56))[0]!=boot->BootDiskIdLow||((ulong*)(header+56))[1]!=boot->BootDiskIdHigh)return false;
byte* copy=stackalloc byte[92];for(uint i=0;i<92;i++)copy[i]=header[i];*(uint*)(copy+16)=0;
if(Crc(copy,92)!=*(uint*)(header+16)||*(uint*)(header+80)!=128||*(uint*)(header+84)!=128)return false;
ulong entries=*(ulong*)(header+72);uint crc=*(uint*)(header+88);
if(!Sectors(m,entries,32,false)||Crc(At(m->Data),16384)!=crc)return false;
byte* table=At(m->Data);
for(uint i=0;i<128;i++)
{
byte* entry=table+i*128;
if(((ulong*)(entry+16))[0]!=boot->BootPartitionIdLow||((ulong*)(entry+16))[1]!=boot->BootPartitionIdHigh)continue;
m->PartitionStart=*(ulong*)(entry+32);m->PartitionEnd=*(ulong*)(entry+40);
return m->PartitionStart>=34&&m->PartitionEnd>=m->PartitionStart&&m->PartitionEnd<m->Blocks;
}
return false;
}
static void DisconnectStorage(UsbDevice* d)
{
for(uint i=0;i<_massCount;i++)if(_mass[i].Device==d){_mass[i].Active=0;if(_mass[i].BootMatch!=0){_mass[i].BootMatch=0;if(_bootMatches!=0)_bootMatches--;}}
if(_bootMass!=null&&_bootMass->Device==d){_writeCount=0;_bootMass=null;}
}
static void ProcessStorageRequest()
{
Native.MemoryBarrier();if(_requestState!=1)return;_requestState=2;
bool ok=false;UsbMassDevice* m=_requestDevice==0xffffffffu?_bootMass:_requestDevice<_massCount?&_mass[_requestDevice]:null;
if(m!=null&&m->Active!=0&&m->Device->Connected!=0&&(_requestDevice==0xffffffffu?_bootMatches==1:m->BootMatch==0))
{
if(_requestKind==3)
{
byte* command=stackalloc byte[16];for(uint i=0;i<16;i++)command[i]=0;command[0]=0x35;
ok=Scsi(m,command,10,0,true);if(ok)_storageFlushes++;
}
else
{
if(_requestKind==2)for(uint i=0;i<_requestCount*512;i++)At(m->Data)[i]=At(_mailbox)[i];
ok=Sectors(m,_requestLba,_requestCount,_requestKind==2);
if(ok&&_requestKind==1)for(uint i=0;i<_requestCount*512;i++)At(_mailbox)[i]=At(m->Data)[i];
}
}
Native.MemoryBarrier();_requestState=_requestState==6?0u:ok?3u:4u;
}
static int Request(uint kind,ulong lba,uint count,byte* buffer,uint device=0xffffffffu)
{
UsbMassDevice* m=device==0xffffffffu?_bootMass:device<_massCount?&_mass[device]:null;
if(m==null||m->Active==0||m->Device->Connected==0||(device==0xffffffffu?_bootMatches!=1:m->BootMatch!=0)||_mailbox==0||kind!=3&&(buffer==null||count==0||count>128))return 0;
ulong irq=Native.IrqSave();if(_requestState!=0){Native.IrqRestore(irq);return 0;}
if(kind==2)for(uint i=0;i<count*512;i++)At(_mailbox)[i]=buffer[i];
_requestDevice=device;_requestKind=kind;_requestLba=lba;_requestCount=count;_requestState=1;Native.MemoryBarrier();Native.IrqRestore(irq);
ulong end=Now()+10000;
for(;;)
{
Native.MemoryBarrier();uint state=_requestState;
if(state==3||state==4)
{
if(state==3&&kind==1)for(uint i=0;i<count*512;i++)buffer[i]=At(_mailbox)[i];
_requestState=0;Native.MemoryBarrier();return state==3?1:0;
}
if(Now()>=end)
{irq=Native.IrqSave();uint current=_requestState;if(current==1)_requestState=0;else if(current==2)_requestState=6;Native.IrqRestore(irq);if(current==3||current==4)continue;return 0;}
Sleep(1);
}
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_info")]
public static ulong StorageInfo(uint field)
{
Native.MemoryBarrier();UsbMassDevice* m=_bootMatches==1?_bootMass:null;
return field switch {0=>1,1=>State,2=>m!=null&&m->Active!=0?1UL:0,3=>m==null?0:m->Blocks,4=>512,
5=>m==null?0:m->PartitionStart,6=>m==null?0:m->PartitionEnd,7=>_storageReads,8=>_storageWrites,9=>_storageFlushes,
10=>_writeStart,11=>_writeCount,12=>Kernel.Boot->BootDiskIdLow,13=>Kernel.Boot->BootDiskIdHigh,
14=>Kernel.Boot->BootPartitionIdLow,15=>Kernel.Boot->BootPartitionIdHigh,_=>0};
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_read")]
public static int StorageRead(ulong lba,uint count,byte* buffer)=>Request(1,lba,count,buffer);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_write")]
public static int StorageWrite(ulong lba,uint count,byte* buffer)=>Request(2,lba,count,buffer);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_flush")]
public static int StorageFlush()=>Request(3,0,0,null);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_storage_allow_workspace")]
public static int AllowWorkspace(ulong lba,uint count)
{
if(_bootMatches!=1||_bootMass==null||_writeCount!=0||count!=131072||lba<_bootMass->PartitionStart||lba>_bootMass->PartitionEnd||count>_bootMass->PartitionEnd-lba+1)return 0;
byte* descriptor=stackalloc byte[512];if(Request(1,lba,1,descriptor)!=1)return 0;
if(*(ulong*)descriptor!=0x5357425355454658UL||*(uint*)(descriptor+8)!=1||*(uint*)(descriptor+12)!=count||
*(uint*)(descriptor+16)!=65535||*(uint*)(descriptor+20)!=Crc(descriptor,20))return 0;
_writeStart=lba+1;_writeCount=131070;Native.MemoryBarrier();return 1;
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_info")]
public static ulong OtherInfo(uint index,uint field)
{
Native.MemoryBarrier();if(field==0)return State;if(field==1)return _massCount;if(index>=_massCount)return 0;
UsbMassDevice* m=&_mass[index];return field switch {2=>m->Active!=0&&m->Device->Connected!=0&&m->BootMatch==0?1UL:0,3=>m->Blocks,_=>0};
}
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_read")]
public static int OtherRead(uint index,ulong lba,uint count,byte* buffer)=>Request(1,lba,count,buffer,index);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_write")]
public static int OtherWrite(uint index,ulong lba,uint count,byte* buffer)=>Request(2,lba,count,buffer,index);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_flush")]
public static int OtherFlush(uint index)=>Request(3,0,0,null,index);
[UnmanagedCallersOnly(EntryPoint="xfe_usb_other_allow_partition")]
public static int OtherAllow(uint index,ulong start,ulong count)
{
if(index>=_massCount||_mass[index].Active==0||_mass[index].BootMatch!=0||start==0||count==0||start>=_mass[index].Blocks||count>_mass[index].Blocks-start)return 0;
ulong irq=Native.IrqSave();if(_requestState!=0){Native.IrqRestore(irq);return 0;}
_mass[index].AllowedStart=start;_mass[index].AllowedCount=count;Native.MemoryBarrier();Native.IrqRestore(irq);return 1;
}
}