// AddrSpace // // These are the interfaces through which operations are performed on // virtual and physical address spaces. // namespace Mem; // There are two main kinds of mappable objects: // 1. Objects that can be mapped directly into physical address space. // This includes memory, device I/O, nested AddrSpaces, etc. Objects // with this capability implement the Mappable interface. // 2. Objects that are mapped by copying the content into RAM // (disks, remote objects, dynamically generated data, etc.) // These objects implement the Cacheable interface. // // Type #1 is simple; all it needs to do is translate mappable offsets // into physical. Type #2 requires an intervening Cache to manage the // backing RAM store. Memory-like type #1 objects should also implement // Cacheable so that they can be used remotely. // // Address spaces may be stacked, such that (for example) a debugger maps // an application which maps a file which maps a filesystem which maps a // RAID volume which maps several physical disks; only the disks would be // represented by Caches, everything else by AddrSpaces. Changes at any // level immediately (well, before the operation finishes) propagate up // to the page tables higher levels, and clone() will work at any level // (and can make either the old or new address space the anonymous- // memory-using "shadow" object). Stacked address spaces can also be // used to implement access control. struct Region inline { // Both bounds are inclusive ulong start, end; }; struct RegionWithOffset inline : Region { ulong offset; }; bitfield AccessFlags:3 { // Allow reads to this page. On some platforms, this is assumed // if writes are allowed, due to hardware limitations. Read, // Allow writes to this page. Write, // Allow execution of code from this page. On some platforms, // this is assumed if reads are allowed, due to hardware // limitations. Exec }; interface BlockObject { guid: "4425AF91-52BE-11DA-BD60-000A95BB581A"; // Returns the current size in blocks of the object. get_size(ulong size out); // Returns the object's block size in bytes. Requests to map, read, // write, etc. this object must be done with this granularity. The // block size must be a power of two. get_block_size(ulong block_size out); }; // Finer grained mapping than the block size may be done via the virtual // address space, but the AddrSpace will always request object->physical // mappings that are a multiple of the block size. interface Mappable : BlockObject { guid: "B8E7A0DF-EAB6-11D9-BEFB-000A95BB581A"; }; interface Cacheable : BlockObject { guid: "BB4C729D-EAB6-11D9-ADA2-000A95BB581A"; // Add the pages in the specified region to the specified cache. // The start and end of the region will be aligned to the larger // of the page size and the block size. fill(Cache cache, Region region) async; }; // This is the Mappable used to map a Cacheable object. // Calling get_subspace on a Cache returns a pure Mappable; holders of // such a reference alone can only map it, not add or remove pages or // change its cacheable. interface Cache : Mappable { guid: "BD9A04F5-EAB6-11D9-A420-000A95BB581A"; // Get and set the object mapped through this Cache. Any other // methods when obj is null (or never set) throw an InvalidState // exception. Calling set_cacheable while this Cache has any // current mappings throws an InvalidState exception. set_cacheable(Cacheable obj); get_cacheable(Cacheable obj out); // Add one or more pages to the cache. Addr must be page-aligned, and // the size of buf must be a multiple of the page size. fill(ulong addr, octet[] buf push) async; // Add one or more zero-filled pages to the cache. The region must // start and end on a page boundary. fill_zero(Region region) async; // Remove one or more pages from the cache. The region must start and // end on a page boundary. It is not an error to remove pages that are // not in the cache. remove(Region region); // Wait until all of the pages in the region have been added to the // cache. It is the caller's responsibility to ensure that the adding // has been requested, and that blocking will not cause a deadlock. // If there are multiple pages in the region, it is as if this method // were called sequentially on each page; the kernel will not check // whether previously checked pages have been removed while waiting // for later pages. block_on_region(Region region); // Like block_on_region, but returning a blocker suitable for use // with Thread.block_multi. region_blocker(Region region, Proc.Blocker blocker out); }; bitfield AllocFlags { Zero, // Zero out any freshly allocated memory. Insecure, // It is not necessary to zero out the page after it // is freed, unless the next allocator requests it. Commit, // Commit the allocation to actual memory or // swap, failing if this cannot be done. Lock, // Only allocate actual memory, and lock it against // swapping. Fails if not enough actual RAM is // available (either in general or in the caller's // locked RAM quota). If Lock is set, Commit is // ignored. NoZeroLocal, // Accept secure pages from the current address // space without first zeroing. Ignored if Zero is // specified. Overmap should be set to None when // this is used, and every byte should be // overwritten before data in any such pages is // passed to another address space. Paranoid, // Zero pages even when going to a NoZeroLocal allocator // in the current address space. Use this for pages which // are particularly likely to contain sensitive data. }; bitfield MapFlags { Fixed, // Fail if the exact starting address is unavailable. // Otherwise, if the supplied starting address // unavailable, the address space manager allocates a // free region and returns it in "start". If this // behavior is explicitly desired (as it ususally is), // "start" should contain all bits set, which is always // invalid. For non-process (stacked) address spaces, // this flag is treated as always set. Replace, // Atomically replace any existing mapping with the new // mapping, rather than fail. This flag is only // meaningful if Fixed is set. CopyOnWrite, // Share the mapped object only until it is written to; // then, before the write takes place, copy the object. // It is undefined whether this mapping will receive // the copy or the original. Snapshot, // The mapped object will also be made CopyOnWrite, so // that any writes to the mapped page via any mapping // will cause a fault. Thus, after the snapshot, the // only changes that will be visible will be through // the new mapping. This is ideal for things like fork() // and file versioning, and is used by AddrSpace.clone(). // // If not set, then only the new mapping will be // CopyOnWrite, so if another mapping updates the page // before a write occurs through this mapping (thus // breaking CopyOnWrite), the change will be visible in // the new mapping. This is ideal for private // mappings, where all that is desired is that the new // mapping cannot change the underlying object (while // keeping the mapping writeable). // // Ignored if CopyOnWrite is not set. AccessFlags access:3, // These are the requested read/write/execute // permissions on the mapping. A missing permission (or // unmapped page) at any level in an address space stack // will cause a MemoryFault, even if the page is mapped // with the needed permissions at the top level. The // map() call will not fail due to such a condition. enum OverMap:2 { None, // Never map non-argument memory to a callee's address // space. ReadOnly, // Allow read-only mappings of non-argument data within // the same page as an argument into the callee's // address space. ReadWrite // Allow all mappings of non-argument data within the // same page as an argument into the callee's address // space. } overmap }; interface AddrSpace { guid: "BF9D2070-EAB6-11D9-A2D2-000A95BB581A"; const ulong unspecified_start = 0xffffffffffffffff; // Return the mappable associated with this address space, which can // be used to allow another address space to stack with this one. // The mappable handle only allows pages to be mapped; it does not // allow any changes to the mappings, nor can a handle to the AddrSpace // be obtained from it. This method must always return the same // Mappable object when called on the same AddrSpace. get_mappable(Mappable ma out); // Create a new AddrSpace that is a copy-on-write clone of this AddrSpace. // This method is used to implement fork() and in-memory file versioning, // and could also be used to assist garbage collection and other purposes. // // By default, the old address space continues to be backed by // whatever Mappables were in use, and pages in the new address space // are backed by anonymous memory when a page in either is written to. // If flags.Reverse is true, though, this is reversed, which is useful // when versioning a file to make the new version the one that gets // stored to disk. // // The upstream address space is also marked as copy-on clone(AddrSpace addrspace out, CloneFlags flags); bitfield CloneFlags { Reverse }; // Mappable must be implemented by the local kernel, and must hold // read/write/exec permissions appropriate for the MapFlags given. map(Mappable ma, Region region, ulong vstart inout, MapFlags flags); unmap(Region region); // Set the flags on all pages in the region. CopyOnWrite can be // set, but not cleared, using this method. Fixed is ignored. set_mapflags(Region region, MapFlags flags); // Returns the flags on the given region, if all pages have the // same flags (except for CopyOnWrite, which is not returned by // this method, as it can be asynchronously cleared). get_mapflags(Region region, MapFlags flags out, bool all_same out); // Returns the Mappable that covers the specified range, and the offset // into the Mappable that corresponds to the first page in the region. // If any pages within the range are not mapped, or if more than one // Mappable is mapped within the region, or if the offsets into the // Mappable are not contiguous, then NULL is returned in ma. // // This is used to implement mremap(). get_mapping(Region region, Mappable ma out, ulong offset out); // Returns the minimum page size (and thus mapping size/alignment) // supported in this address space. An attempt to create a mapping // that violates this will result in an InvalidArgument exception. get_page_size(uint page_size out); // Returns the minimum alignment supported for mapping requests; // (vstart % min_align) must equal (region.start % min_align). This // is at least the minimum page size, but may be more on certain // hardware, such as virtually-indexed-physically-tagged caches, where // larger alignment is needed to ensure the absence of cache aliases. // An attempt to create a mapping that violates this will result in an // InvalidArgument exception. get_min_align(uint min_align out); }; interface AllocHandle { guid: "CB029266-EAB6-11D9-BCA0-000A95BB581A"; get_regions(Region[] regions out); // Free a portion of an allocation. To free all of an allocation, // simply release all references to the handle. Each region shall // start and end on block size boundaries. Throws OperationNotSupported // if the allocator does not support partial frees. free(Region[] regions); }; interface Allocator { guid: "CCF5D83C-EAB6-11D9-8BB7-000A95BB581A"; const ulong unspecified_start = 0xffffffffffffffff; bitfield AllocFlags { // If set, fail if the exact starting address is unavailable (or if // the allocator does not support caller-supplied starting // addresses). // // Otherwise, if the supplied starting address unavailable, the // allocator allocates a free region and returns it in "start". If // this behavior is explicitly desired (as it ususally is), "start" // should be set to unspecified_start, which is always invalid. // Using unspecified_start may be faster than specifying other // invalid addresses (as the allocator can check for this value // rather than checking address availability), and zero should not // be used for this purpose as it may be a valid address. Fixed }; alloc(ulong start inout, ulong len, AllocFlags flags, AllocHandle handle out); }; interface GenericAllocator : Allocator { guid: "D033DD3A-EAB6-11D9-96E4-000A95BB581A"; // Set the minimal block size used by the allocator. // This can only be done before any alloc() or // add_regions() calls; an InvalidState exception may // be thrown otherwise. set_block_size(ulong block_size); // Make one or more regions available for allocations. // The regions may not overlap each other or any existing // regions (whether or not allocated). Regions may not // be removed once added using this interface; allocators // may optionally provide a mechanism for doing so. add_regions(Region[] regions); };