/* * Load the credential for an application. * * Currently ExpressOS does not deal with key management, etc. * It uses a static key for all application. */ internal static Credential GetCredential(ASCIIString name, Process process) { Contract.Ensures(Contract.Result<Credential>().GhostOwner == process); var key = new byte[] { 0xc8, 0x43, 0xae, 0x85, 0x9b, 0x63, 0x4b, 0x72, 0x1b, 0x14, 0xcf, 0x6d, 0xa8, 0xf9, 0x6f, 0x1d }; // 1003 == AID_GRAPHICS return new Credential(process, 1003, key); }
// Create an empty user-space memory region // reserve the first page as well as the kernel space public static MemoryRegion CreateUserSpaceRegion(Process owner) { Contract.Ensures(Contract.Result<MemoryRegion>().GhostOwner == owner); var r = new MemoryRegion(owner, 0, 0, null, 0, 0, Pointer.Zero, Arch.ArchDefinition.PageSize, true); var r1 = new MemoryRegion(owner, 0, 0, null, 0, 0, new Pointer(AddressSpace.KERNEL_OFFSET), AddressSpace.KERNEL_SIZE, true); r.Next = r1; return r; }
internal static CachePage Allocate(Process owner, int loc) { Contract.Requires(loc >= 0); Contract.Ensures(Contract.Result<CachePage>().CurrentState == State.Empty); Contract.Ensures(Contract.Result<CachePage>().Next == null); var buf = AllocFreeBuffer(); var p = new CachePage(owner, loc, buf); return p; }
public AddressSpace(Process owner, Arch.ArchAddressSpace impl) { Contract.Ensures(GhostOwner == owner); Contract.Ensures(Head.GhostOwner == GhostOwner); this.impl = impl; workingSet = new TableWorkingSet(); Head = MemoryRegion.CreateUserSpaceRegion(owner); this.GhostOwner = owner; }
public File(Process owner, GenericINode inode, int flags, int mode) { Contract.Ensures(GhostOwner == owner); this.inode = inode; this.flags = flags; this.mode = mode; this.position = 0; this.GhostOwner = owner; inode.IncreaseRefCount(); }
private Thread(Arch.ArchThread impl, Process parent) { Contract.Requires(parent != null); Contract.Ensures(Parent == parent); Contract.Ensures(VBinderState.Owner == this); this.impl = impl; this.Parent = parent; this.TLSArray = Arch.NativeMethods.l4api_tls_array_alloc(); this.VBinderState = new VBinderThreadState(this); }
public static int Parse(int helperPid, File file, Process proc, ref UserPtr stackTop) { Contract.Requires(file.GhostOwner == proc); Contract.Requires(proc.Space.GhostOwner == proc); var buf = new byte[Size]; uint pos = 0; if (file.Read(buf, ref pos) != buf.Length) return -ErrorCode.EINVAL; var eh = Read(buf); if (eh.type != ELF32Header.ELF_TYPE_EXECUTABLE || eh.ProgramHeaderOffest == 0) return -ErrorCode.ENOEXEC; proc.EntryPoint = eh.EntryPoint; ELF32ProgramHeader ph = new ELF32ProgramHeader(); var ret = FindInterpreter(file, eh, ref ph); if (ret == -ErrorCode.EINVAL) { Arch.Console.WriteLine("Malformed ELF file"); return ret; } else if (ret == 0) { var interpreterBuf = new byte[ph.FileSize]; pos = ph.offset; if (file.Read(interpreterBuf, ref pos) != interpreterBuf.Length) return -ErrorCode.EINVAL; var interpreterName = new ASCIIString(interpreterBuf); ErrorCode ec; var interpreter_inode = Arch.ArchFS.Open(helperPid, interpreterName, 0, 0, out ec); if (interpreter_inode == null) return -ErrorCode.ENOENT; var interpreter = new File(proc, interpreter_inode, FileFlags.ReadOnly, 0); /* * Parse the information of linker. * * This function will also override the entry point. */ if (Parse(helperPid, interpreter, proc, ref stackTop) != 0) return -ErrorCode.EINVAL; // So now let's copy the program header to the top of the stack, and push auxlirary vectors PushProgramHeaderAndAuxliraryVectors(proc, file, eh, ref stackTop); } return MapInSegments(file, proc, eh); }
public FileDescriptorTable(Process owner) { Contract.Ensures(GhostOwner == owner); descriptors = new File[DEFAULT_SIZE]; for (var i = 0; i < descriptors.Length; ++i) { descriptors[i] = null; } finger = FD_START; this.GhostOwner = owner; }
private CachePage(Process owner, int location, ByteBufferRef buf) { Contract.Requires(location >= 0); Contract.Ensures(Owner == owner); Contract.Ensures(Location == location); Contract.Ensures(CurrentState == State.Empty); Contract.Ensures(Next == null); this.Owner = owner; this.Location = location; this.Buffer = buf; this.Next = null; this.CurrentState = State.Empty; }
public void OnActiveProcessChanged(Process newActiveProcess) { Contract.Requires(newActiveProcess != null); Contract.Ensures(ActiveProcess == newActiveProcess); if (ActiveProcess == newActiveProcess) return; if (ActiveProcess != null) { if (ActiveProcess.ScreenEnabled) DisableScreen(); ActiveProcess = null; } EnableScreen(newActiveProcess); }
internal MemoryRegion(Process owner, uint access, int flags, File file, uint fileOffset, int fileSize, Pointer vaddr, int size, bool isFixed) { Contract.Requires(file == null || file.GhostOwner == owner); Contract.Ensures(GhostOwner == owner); this.Access = access; this.Flags = flags; this.BackingFile = file; this.FileOffset = fileOffset; this.FileSize = fileSize; this.StartAddress = vaddr; this.Size = size; this.Next = null; this.IsFixed = isFixed; this.GhostOwner = owner; if (file != null) file.inode.IncreaseRefCount(); }
internal unsafe int Write(Process proc, uint[] val) { fixed (uint *p = &val[0]) { return Write(proc, new Pointer(p), val.Length * sizeof(uint)); } }
internal int Write(Process proc, byte[] val) { var b = new ByteBufferRef(val); return Write(proc, b); }
internal unsafe int Write(Process proc, int v) { var p = v; return Write(proc, new Pointer(&p), sizeof(int)); }
// // We do sync read for this one, since it's simpler.. // public static Process CreateProcess(ASCIIString path, ASCIIString[] argv, ASCIIString[] envp, AndroidApplicationInfo appInfo) { var proc = new Process(path, appInfo); Utils.Assert(!proc.Space.impl._value.isInvalid); uint addr; int workspace_fd; uint workspace_size; proc.helperPid = Arch.IPCStubs.linux_sys_take_helper(out addr, out workspace_fd, out workspace_size); if (proc.helperPid < 0) { Arch.Console.WriteLine("CreateProcess: cannot get helper"); return null; } proc.ShadowBinderVMStart = addr; ErrorCode ec; var inode = Arch.ArchFS.Open(proc.helperPid, path, 0, 0, out ec); if (inode == null) { Arch.Console.WriteLine("CreateProcess: cannot open file"); return null; } var stack_top = new UserPtr(INITIAL_STACK_LOCATION); // 4M Initial stack var stack_size = 4096 * Arch.ArchDefinition.PageSize; proc.Space.AddStackMapping(stack_top, stack_size); stack_top += stack_size; var augmented_envp = CreateEnvpArrayWithWorkspace(envp, proc, workspace_fd, workspace_size); var envp_ptr = PushCharArray(proc, augmented_envp, ref stack_top); if (envp_ptr == null) { Arch.Console.WriteLine("CreateProcess: Push envp failed"); return null; } var argv_ptr = PushCharArray(proc, argv, ref stack_top); if (argv_ptr == null) { Arch.Console.WriteLine("CreateProcess: Push argv failed"); return null; } stack_top = UserPtr.RoundDown(stack_top); // Parse the ELF file, which might push additional info on to the stack // (i.e., aux vectors when the ELF is dynamically linked) var file = new File(proc, inode, FileFlags.ReadWrite, 0); int ret = ELF32Header.Parse(proc.helperPid, file, proc, ref stack_top); if (ret != 0) { Arch.Console.WriteLine("CreateProcess: Parse ELF file failed"); return null; } //%esp The stack contains the arguments and environment: // 0(%esp) argc // 4(%esp) argv[0] // ... // (4*argc)(%esp) NULL // (4*(argc+1))(%esp) envp[0] // ... // NULL if (PushArgumentPointers(proc, envp_ptr, ref stack_top) != 0) return null; if (PushArgumentPointers(proc, argv_ptr, ref stack_top) != 0) return null; if (PushInt(proc, argv_ptr.Length, ref stack_top) != 0) return null; // Stdio var file_stdout = File.CreateStdout(proc); Contract.Assume(proc.Files.IsAvailableFd(Process.STDOUT_FD)); proc.InstallFd(Process.STDOUT_FD, file_stdout); var file_stderr = File.CreateStdout(proc); Contract.Assume(proc.Files.IsAvailableFd(Process.STDERR_FD)); proc.InstallFd(Process.STDERR_FD, file_stderr); var mainThread = Thread.Create(proc); if (appInfo != null) { var p = appInfo.ToParcel(); Globals.LinuxIPCBuffer.CopyFrom(0, p); Arch.IPCStubs.WriteAppInfo(proc.helperPid, p.Length); } // Start the main thread mainThread.Start(new Pointer(proc.EntryPoint), stack_top.Value); return proc; }
private static int PushProgramHeaderAndAuxliraryVectors(Process proc, File file, ELF32Header eh, ref UserPtr stackTop) { var programHeaderLength = eh.NumOfProgramHeader * eh.ProgramHeaderSize; var buf = new byte[programHeaderLength]; uint pos = eh.ProgramHeaderOffest; if (file.Read(buf, ref pos) != programHeaderLength) return -ErrorCode.ENOMEM; stackTop -= programHeaderLength; UserPtr ph_ptr = stackTop; if (ph_ptr.Write(proc, buf) != 0) return -ErrorCode.ENOMEM; // align stackTop = UserPtr.RoundDown(stackTop); var aux_vector = new uint[LengthOfAuxVector]; aux_vector[0] = AT_PHDR; aux_vector[1] = ph_ptr.Value.ToUInt32(); aux_vector[2] = AT_ENTRY; aux_vector[3] = eh.EntryPoint; aux_vector[4] = AT_PHNUM; aux_vector[5] = eh.NumOfProgramHeader; aux_vector[6] = 0; aux_vector[7] = 0; var auxVectorSize = sizeof(uint) * LengthOfAuxVector; stackTop -= auxVectorSize; if (stackTop.Write(proc, aux_vector) != 0) return -ErrorCode.ENOMEM; return 0; }
int Write(Process proc, ByteBufferRef val) { return Write(proc, new Pointer(val.Location), val.Length); }
private static ASCIIString[] CreateEnvpArrayWithWorkspace(ASCIIString[] envp, Process proc, int workspace_fd, uint workspace_size) { var res = new ASCIIString[envp.Length + 1]; for (int i = 0; i < envp.Length; ++i) res[i] = envp[i]; var inode = new Arch.ArchINode(workspace_fd, workspace_size, proc.helperPid); var file = new File(proc, inode, FileFlags.ReadOnly, 0); var fd = proc.GetUnusedFd(); proc.InstallFd(fd, file); var s = "ANDROID_PROPERTY_WORKSPACE=" + fd.ToString() + "," + workspace_size.ToString(); res[envp.Length] = new ASCIIString(s); return res; }
private void DisableScreen() { Contract.Requires(ActiveProcess != null); Contract.Requires(ActiveProcess.ScreenEnabled); Contract.Ensures(ActiveProcess == null); Contract.Ensures(!Contract.OldValue(ActiveProcess).ScreenEnabled); for (var r = ActiveProcess.Space.Head; r != null; r = r.Next) { if (r.BackingFile == null || r.BackingFile.inode.kind != GenericINode.INodeKind.ScreenBufferINodeKind) continue; r.UpdateAccessRights(ActiveProcess.Space, MemoryRegion.FAULT_READ); } ActiveProcess.ScreenEnabled = false; ActiveProcess = null; }
private void EnableScreen(Process proc) { Contract.Requires(proc != null); Contract.Requires(ActiveProcess == null); Contract.Ensures(ActiveProcess == proc); Contract.Ensures(ActiveProcess.ScreenEnabled); for (var r = proc.Space.Head; r != null; r = r.Next) { if (r.BackingFile == null || r.BackingFile.inode.kind != GenericINode.INodeKind.ScreenBufferINodeKind) continue; r.UpdateAccessRights(proc.Space, MemoryRegion.FAULT_WRITE | MemoryRegion.FAULT_READ); } ActiveProcess = proc; ActiveProcess.ScreenEnabled = true; }
internal static File CreateStdout(Process proc) { Contract.Ensures(Contract.Result<File>().GhostOwner == proc); return new File(proc, ConsoleINode.Instance, FileFlags.WriteOnly, 0); }
internal static Thread Create(Process parent) { var impl = Arch.ArchThread.Create(parent.Space.impl); if (impl == null) { Arch.Console.WriteLine("create thread failed"); return null; } var res = new Thread(impl, parent); Globals.Threads.Add(res); return res; }
private int Read(Process process, ByteBufferRef buffer, bool is_string) { var bytesLeft = is_string ? buffer.Length - 1 : buffer.Length; var bytesRead = 0; var src = _value; while (bytesLeft > 0) { var region = process.Space.Find(src); // Invalid mapping if (region == null || region.IsFixed) break; var off = Arch.ArchDefinition.PageOffset(src.ToUInt32()); var virtualAddr = process.Space.UserToVirt(new UserPtr(src)); if (virtualAddr == Pointer.Zero) { uint permission; Pager.HandlePageFault(process, MemoryRegion.FAULT_MASK, _value, Pointer.Zero, out virtualAddr, out permission); if (virtualAddr == Pointer.Zero) break; } var virtual_page = Arch.ArchDefinition.PageIndex(virtualAddr.ToUInt32()); var page_buf = new ByteBufferRef(new IntPtr(virtual_page), Arch.ArchDefinition.PageSize); var b = Arch.ArchDefinition.PageSize - off; var bytesTobeCopied = b > bytesLeft ? bytesLeft : b; var src_buf = page_buf.Slice(off, bytesTobeCopied); if (is_string) { var res = Util.CopyString(ref buffer, bytesRead, src_buf); bytesRead += res; if (res < bytesTobeCopied) { // We're done. break; } } else { for (var i = 0; i < bytesTobeCopied; ++i) { buffer.Set(i + bytesRead, src_buf.Get(i)); } bytesRead += bytesTobeCopied; } bytesLeft -= bytesTobeCopied; src += bytesTobeCopied; } if (is_string) { buffer.Set(bytesRead, 0); } return bytesRead; }
private int Write(Process process, Pointer dst, int length) { var bytesLeft = length; var src = _value; var dst_buf = new ByteBufferRef(dst.ToIntPtr(), length); var cursor = 0; while (bytesLeft > 0) { var region = process.Space.Find(src); // Invalid mapping if (region == null || region.IsFixed) { return bytesLeft; } var off = Arch.ArchDefinition.PageOffset(src.ToUInt32()); var virtualAddr = process.Space.UserToVirt(new UserPtr(src)); if (virtualAddr == Pointer.Zero) { // Page isn't present, try to bring it in. uint permission; Pager.HandlePageFault(process, MemoryRegion.FAULT_MASK, src, Pointer.Zero, out virtualAddr, out permission); if (virtualAddr == Pointer.Zero) break; } var virtual_page = Arch.ArchDefinition.PageIndex(virtualAddr.ToUInt32()); var page_buf = new ByteBufferRef(new IntPtr(virtual_page), Arch.ArchDefinition.PageSize); var b = Arch.ArchDefinition.PageSize - off; var bytesTobeCopied = b > bytesLeft ? bytesLeft : b; var dst_buf_page = page_buf.Slice(off, bytesTobeCopied); for (var i = 0; i < bytesTobeCopied; ++i) { dst_buf_page.Set(i, dst_buf.Get(cursor + i)); } bytesLeft -= bytesTobeCopied; src += bytesTobeCopied; cursor += bytesTobeCopied; } return bytesLeft; }
private static int PushArgumentPointers(Process proc, UserPtr[] arr, ref UserPtr stack_top) { if (PushInt(proc, 0, ref stack_top) != 0) return -1; for (int i = arr.Length - 1; i >= 0; --i) { var p = arr[i]; if (PushInt(proc, p.Value.ToInt32(), ref stack_top) != 0) return -1; } return 0; }
private static int MapInSegments(File file, Process proc, ELF32Header eh) { Contract.Requires(file != null && file.GhostOwner == proc); Contract.Requires(proc.Space.GhostOwner == proc); var buf = new byte[ELF32ProgramHeader.Size]; var ph = new ELF32ProgramHeader(); // At this point we need to map in all stuff in PT_LOAD for (var i = 0; i < eh.NumOfProgramHeader; ++i) { var pos = (uint)(eh.ProgramHeaderOffest + eh.ProgramHeaderSize * i); if (file.Read(buf, ref pos) != buf.Length) return -ErrorCode.EINVAL; ph = ELF32ProgramHeader.Read(buf); var size = ph.FileSize > ph.MemorySize ? ph.FileSize : ph.MemorySize; if (ph.type != ELF32ProgramHeader.PT_LOAD) continue; // Round address to page boundary var diff = Arch.ArchDefinition.PageOffset(ph.vaddr); var vaddr = new Pointer(ph.vaddr); var offset = ph.offset; var memSize = (int)Arch.ArchDefinition.PageAlign((uint)ph.MemorySize); var fileSize = ph.FileSize; if (diff < 0 || ph.offset < diff || fileSize + diff > file.inode.Size || fileSize <= 0 || memSize <= 0) return -ErrorCode.EINVAL; vaddr -= diff; offset -= (uint)diff; fileSize += diff; if (fileSize > memSize) fileSize = memSize; if (proc.Space.AddMapping(ph.ExpressOSAccessFlag, 0, file, offset, fileSize, vaddr, memSize) != 0) return -ErrorCode.ENOMEM; // Update brk var segmentEnd = (vaddr + memSize).ToUInt32(); if (segmentEnd > proc.Space.Brk) { proc.Space.InitializeBrk(segmentEnd); } } return 0; }
private static UserPtr[] PushCharArray(Process proc, ASCIIString[] arr, ref UserPtr stack_top) { var res = new UserPtr[arr.Length]; for (int i = 0; i < arr.Length; ++i) { // Include the terminator stack_top -= arr[i].Length + 1; res[i] = stack_top; if (stack_top.Write(proc, arr[i].GetByteString()) != 0) return null; } return res; }
private static int PushInt(Process proc, int v, ref UserPtr stack_top) { stack_top -= sizeof(int); if (stack_top.Write(proc, v) != 0) return -1; return 0; }
public static void HandlePageFault(Process process, uint faultType, Pointer faultAddress, Pointer faultIP, out Pointer physicalPage, out uint permission) { // Object invariants of Process Contract.Assume(process.Space.GhostOwner == process); Contract.Assume(process.Space.Head.GhostOwner == process); // Never map page 0 if (faultAddress.ToUInt32() < Arch.ArchDefinition.PageSize) { physicalPage = Pointer.Zero; permission = MemoryRegion.FALUT_NONE; return; } SyscallProfiler.EnterPageFault(); var space = process.Space; var region = space.Find(faultAddress); if (region != null && (faultType & region.Access) != 0) { /* * Check whether the kernel has allocated a page for this address, * which might be the case due to UserPtr.Read() / UserPtr.Write(). */ var mapped_in_page = space.UserToVirt(new UserPtr(faultAddress)); if (mapped_in_page != Pointer.Zero) { physicalPage = PageIndex(mapped_in_page); permission = region.Access & MemoryRegion.FAULT_MASK; return; } // If the page is a shared page from Linux, we'll need to ask linux to grab the page // Otherwise let's just allocate a fresh one. var shared_memory_region = IsAlienSharedRegion(region); var ghost_page_from_fresh_memory = false; ByteBufferRef buf; if (shared_memory_region) { buf = Globals.LinuxMemoryAllocator.GetUserPage(process, faultType, ToShadowProcessAddress(faultAddress, region)); if (!buf.isValid) { Arch.Console.WriteLine("pager: cannot map in alien page."); space.DumpAll(); physicalPage = Pointer.Zero; permission = MemoryRegion.FALUT_NONE; return; } } else { buf = Globals.PageAllocator.AllocPage(); ghost_page_from_fresh_memory = true; if (!buf.isValid) { Arch.Console.WriteLine("Cannot allocate new pages"); Utils.Panic(); } if (region.BackingFile != null) { var rel_pos = (PageIndex(faultAddress) - region.StartAddress); uint pos = (uint)((ulong)rel_pos + region.FileOffset); var readSizeLong = region.FileSize - rel_pos; if (readSizeLong < 0) readSizeLong = 0; else if (readSizeLong > Arch.ArchDefinition.PageSize) readSizeLong = Arch.ArchDefinition.PageSize; int readSize = (int)readSizeLong; Contract.Assert(region.BackingFile.GhostOwner == process); var r = region.BackingFile.Read(buf, 0, readSize, ref pos); if (r < 0) r = 0; Utils.Assert(r <= Arch.ArchDefinition.PageSize); if (r < Arch.ArchDefinition.PageSize) buf.ClearAfter(r); } else { buf.Clear(); } } Contract.Assert(shared_memory_region ^ ghost_page_from_fresh_memory); var page = new Pointer(buf.Location); space.AddIntoWorkingSet(new UserPtr(PageIndex(faultAddress)), page); SyscallProfiler.ExitPageFault(); physicalPage = page; permission = region.Access & MemoryRegion.FAULT_MASK; return; } else { /* * TODO: mmap2 enables the application requests an automatically expandable * region (e.g., a stack) * * The feature doesn't seem to be actively used by the applications, since * both the C runtime and the pthread library initializes stack explicitly. * * The feature is currently unimplemented. */ } physicalPage = Pointer.Zero; permission = MemoryRegion.FALUT_NONE; return; }