|
@@ -1,29 +1,525 @@
|
|
|
package darwin
|
|
|
|
|
|
-foreign import pthread "system:System.framework"
|
|
|
+foreign import mach "system:System.framework"
|
|
|
|
|
|
import "core:c"
|
|
|
+import "base:intrinsics"
|
|
|
|
|
|
-// NOTE(tetra): Unclear whether these should be aligned 16 or not.
|
|
|
-// However all other sync primitives are aligned for robustness.
|
|
|
-// I cannot currently align these though.
|
|
|
-// See core/sys/unix/pthread_linux.odin/pthread_t.
|
|
|
-task_t :: distinct u64
|
|
|
-semaphore_t :: distinct u64
|
|
|
+kern_return_t :: distinct c.int
|
|
|
|
|
|
-kern_return_t :: distinct u64
|
|
|
-thread_act_t :: distinct u64
|
|
|
+mach_port_t :: distinct c.uint
|
|
|
+vm_map_t :: mach_port_t
|
|
|
+mem_entry_name_port_t :: mach_port_t
|
|
|
+ipc_space_t :: mach_port_t
|
|
|
+thread_t :: mach_port_t
|
|
|
+task_t :: mach_port_t
|
|
|
+semaphore_t :: mach_port_t
|
|
|
+
|
|
|
+vm_size_t :: distinct c.uintptr_t
|
|
|
+
|
|
|
+vm_address_t :: vm_offset_t
|
|
|
+vm_offset_t :: distinct c.uintptr_t
|
|
|
+
|
|
|
+// NOTE(beau): typedefed to int in the original headers
|
|
|
+boolean_t :: b32
|
|
|
+
|
|
|
+vm_prot_t :: distinct c.int
|
|
|
+
|
|
|
+vm_inherit_t :: distinct c.uint
|
|
|
+
|
|
|
+mach_port_name_t :: distinct c.uint
|
|
|
+
|
|
|
+sync_policy_t :: distinct c.int
|
|
|
|
|
|
@(default_calling_convention="c")
|
|
|
-foreign pthread {
|
|
|
- mach_task_self :: proc() -> task_t ---
|
|
|
+foreign mach {
|
|
|
+ mach_task_self :: proc() -> mach_port_t ---
|
|
|
+
|
|
|
+ semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return ---
|
|
|
+ semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return ---
|
|
|
+
|
|
|
+ semaphore_signal :: proc(semaphore: semaphore_t) -> Kern_Return ---
|
|
|
+ semaphore_signal_all :: proc(semaphore: semaphore_t) -> Kern_Return ---
|
|
|
+ semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_t) -> Kern_Return ---
|
|
|
+
|
|
|
+ semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return ---
|
|
|
+
|
|
|
+ vm_allocate :: proc (target_task : vm_map_t, address: ^vm_address_t, size: vm_size_t, flags: VM_Flags) -> Kern_Return ---
|
|
|
+
|
|
|
+ vm_deallocate :: proc(target_task: vm_map_t, address: vm_address_t, size: vm_size_t) -> Kern_Return ---
|
|
|
+
|
|
|
+ vm_map :: proc(
|
|
|
+ target_task: vm_map_t,
|
|
|
+ address: ^vm_address_t,
|
|
|
+ size: vm_size_t,
|
|
|
+ mask: vm_address_t,
|
|
|
+ flags: VM_Flags,
|
|
|
+ object: mem_entry_name_port_t,
|
|
|
+ offset: vm_offset_t,
|
|
|
+ copy: boolean_t,
|
|
|
+ cur_protection,
|
|
|
+ max_protection: VM_Prot_Flags,
|
|
|
+ inheritance: VM_Inherit,
|
|
|
+ ) -> Kern_Return ---
|
|
|
+
|
|
|
+ mach_make_memory_entry :: proc(
|
|
|
+ target_task: vm_map_t,
|
|
|
+ size: ^vm_size_t,
|
|
|
+ offset: vm_offset_t,
|
|
|
+ permission: VM_Prot_Flags,
|
|
|
+ object_handle: ^mem_entry_name_port_t,
|
|
|
+ parent_entry: mem_entry_name_port_t,
|
|
|
+ ) -> Kern_Return ---
|
|
|
+
|
|
|
+ mach_port_deallocate :: proc(
|
|
|
+ task: ipc_space_t,
|
|
|
+ name: mach_port_name_t,
|
|
|
+ ) -> Kern_Return ---
|
|
|
+
|
|
|
+ vm_page_size: vm_size_t
|
|
|
+}
|
|
|
+
|
|
|
+Kern_Return :: enum kern_return_t {
|
|
|
+ Success,
|
|
|
+
|
|
|
+ /* Specified address is not currently valid.
|
|
|
+ */
|
|
|
+ Invalid_Address,
|
|
|
+
|
|
|
+ /* Specified memory is valid, but does not permit the
|
|
|
+ * required forms of access.
|
|
|
+ */
|
|
|
+ Protection_Failure,
|
|
|
+
|
|
|
+ /* The address range specified is already in use, or
|
|
|
+ * no address range of the size specified could be
|
|
|
+ * found.
|
|
|
+ */
|
|
|
+ No_Space,
|
|
|
+
|
|
|
+ /* The function requested was not applicable to this
|
|
|
+ * type of argument, or an argument is invalid
|
|
|
+ */
|
|
|
+ Invalid_Argument,
|
|
|
+
|
|
|
+ /* The function could not be performed. A catch-all.
|
|
|
+ */
|
|
|
+ Failure,
|
|
|
+
|
|
|
+ /* A system resource could not be allocated to fulfill
|
|
|
+ * this request. This failure may not be permanent.
|
|
|
+ */
|
|
|
+ Resource_Shortage,
|
|
|
+
|
|
|
+ /* The task in question does not hold receive rights
|
|
|
+ * for the port argument.
|
|
|
+ */
|
|
|
+ Not_Receiver,
|
|
|
+
|
|
|
+ /* Bogus access restriction.
|
|
|
+ */
|
|
|
+ No_Access,
|
|
|
+
|
|
|
+ /* During a page fault, the target address refers to a
|
|
|
+ * memory object that has been destroyed. This
|
|
|
+ * failure is permanent.
|
|
|
+ */
|
|
|
+ Memory_Failure,
|
|
|
+
|
|
|
+ /* During a page fault, the memory object indicated
|
|
|
+ * that the data could not be returned. This failure
|
|
|
+ * may be temporary; future attempts to access this
|
|
|
+ * same data may succeed, as defined by the memory
|
|
|
+ * object.
|
|
|
+ */
|
|
|
+ Memory_Error,
|
|
|
+
|
|
|
+ /* The receive right is already a member of the portset.
|
|
|
+ */
|
|
|
+ Already_In_Set,
|
|
|
+
|
|
|
+ /* The receive right is not a member of a port set.
|
|
|
+ */
|
|
|
+ Not_In_Set,
|
|
|
+
|
|
|
+ /* The name already denotes a right in the task.
|
|
|
+ */
|
|
|
+ Name_Exists,
|
|
|
+
|
|
|
+ /* The operation was aborted. Ipc code will
|
|
|
+ * catch this and reflect it as a message error.
|
|
|
+ */
|
|
|
+ Aborted,
|
|
|
+
|
|
|
+ /* The name doesn't denote a right in the task.
|
|
|
+ */
|
|
|
+ Invalid_Name,
|
|
|
+
|
|
|
+ /* Target task isn't an active task.
|
|
|
+ */
|
|
|
+ Invalid_Task,
|
|
|
+
|
|
|
+ /* The name denotes a right, but not an appropriate right.
|
|
|
+ */
|
|
|
+ Invalid_Right,
|
|
|
+
|
|
|
+ /* A blatant range error.
|
|
|
+ */
|
|
|
+ Invalid_Value,
|
|
|
+
|
|
|
+ /* Operation would overflow limit on user-references.
|
|
|
+ */
|
|
|
+ URefs_Overflow,
|
|
|
+
|
|
|
+ /* The supplied (port) capability is improper.
|
|
|
+ */
|
|
|
+ Invalid_Capability,
|
|
|
+
|
|
|
+ /* The task already has send or receive rights
|
|
|
+ * for the port under another name.
|
|
|
+ */
|
|
|
+ Right_Exists,
|
|
|
+
|
|
|
+ /* Target host isn't actually a host.
|
|
|
+ */
|
|
|
+ Invalid_Host,
|
|
|
+
|
|
|
+ /* An attempt was made to supply "precious" data
|
|
|
+ * for memory that is already present in a
|
|
|
+ * memory object.
|
|
|
+ */
|
|
|
+ Memory_Present,
|
|
|
+
|
|
|
+ /* A page was requested of a memory manager via
|
|
|
+ * memory_object_data_request for an object using
|
|
|
+ * a MEMORY_OBJECT_COPY_CALL strategy, with the
|
|
|
+ * VM_PROT_WANTS_COPY flag being used to specify
|
|
|
+ * that the page desired is for a copy of the
|
|
|
+ * object, and the memory manager has detected
|
|
|
+ * the page was pushed into a copy of the object
|
|
|
+ * while the kernel was walking the shadow chain
|
|
|
+ * from the copy to the object. This error code
|
|
|
+ * is delivered via memory_object_data_error
|
|
|
+ * and is handled by the kernel (it forces the
|
|
|
+ * kernel to restart the fault). It will not be
|
|
|
+ * seen by users.
|
|
|
+ */
|
|
|
+ Memory_Data_Moved,
|
|
|
+
|
|
|
+ /* A strategic copy was attempted of an object
|
|
|
+ * upon which a quicker copy is now possible.
|
|
|
+ * The caller should retry the copy using
|
|
|
+ * vm_object_copy_quickly. This error code
|
|
|
+ * is seen only by the kernel.
|
|
|
+ */
|
|
|
+ Memory_Restart_Copy,
|
|
|
+
|
|
|
+ /* An argument applied to assert processor set privilege
|
|
|
+ * was not a processor set control port.
|
|
|
+ */
|
|
|
+ Invalid_Processor_Set,
|
|
|
+
|
|
|
+ /* The specified scheduling attributes exceed the thread's
|
|
|
+ * limits.
|
|
|
+ */
|
|
|
+ Policy_Limit,
|
|
|
+
|
|
|
+ /* The specified scheduling policy is not currently
|
|
|
+ * enabled for the processor set.
|
|
|
+ */
|
|
|
+ Invalid_Policy,
|
|
|
+
|
|
|
+ /* The external memory manager failed to initialize the
|
|
|
+ * memory object.
|
|
|
+ */
|
|
|
+ Invalid_Object,
|
|
|
+
|
|
|
+ /* A thread is attempting to wait for an event for which
|
|
|
+ * there is already a waiting thread.
|
|
|
+ */
|
|
|
+ Already_Waiting,
|
|
|
+
|
|
|
+ /* An attempt was made to destroy the default processor
|
|
|
+ * set.
|
|
|
+ */
|
|
|
+ Default_Set,
|
|
|
+
|
|
|
+ /* An attempt was made to fetch an exception port that is
|
|
|
+ * protected, or to abort a thread while processing a
|
|
|
+ * protected exception.
|
|
|
+ */
|
|
|
+ Exception_Protected,
|
|
|
+
|
|
|
+ /* A ledger was required but not supplied.
|
|
|
+ */
|
|
|
+ Invalid_Ledger,
|
|
|
+
|
|
|
+ /* The port was not a memory cache control port.
|
|
|
+ */
|
|
|
+ Invalid_Memory_Control,
|
|
|
+
|
|
|
+ /* An argument supplied to assert security privilege
|
|
|
+ * was not a host security port.
|
|
|
+ */
|
|
|
+ Invalid_Security,
|
|
|
|
|
|
- semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t ---
|
|
|
- semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t ---
|
|
|
+ /* thread_depress_abort was called on a thread which
|
|
|
+ * was not currently depressed.
|
|
|
+ */
|
|
|
+ Not_Depressed,
|
|
|
|
|
|
- semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t ---
|
|
|
- semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t ---
|
|
|
- semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t ---
|
|
|
-
|
|
|
- semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t ---
|
|
|
+ /* Object has been terminated and is no longer available
|
|
|
+ */
|
|
|
+ Terminated,
|
|
|
+
|
|
|
+ /* Lock set has been destroyed and is no longer available.
|
|
|
+ */
|
|
|
+ Lock_Set_Destroyed,
|
|
|
+
|
|
|
+ /* The thread holding the lock terminated before releasing
|
|
|
+ * the lock
|
|
|
+ */
|
|
|
+ Lock_Unstable,
|
|
|
+
|
|
|
+ /* The lock is already owned by another thread
|
|
|
+ */
|
|
|
+ Lock_Owned,
|
|
|
+
|
|
|
+ /* The lock is already owned by the calling thread
|
|
|
+ */
|
|
|
+ Lock_Owned_Self,
|
|
|
+
|
|
|
+ /* Semaphore has been destroyed and is no longer available.
|
|
|
+ */
|
|
|
+ Semaphore_Destroyed,
|
|
|
+
|
|
|
+ /* Return from RPC indicating the target server was
|
|
|
+ * terminated before it successfully replied
|
|
|
+ */
|
|
|
+ Rpc_Server_Terminated,
|
|
|
+
|
|
|
+ /* Terminate an orphaned activation.
|
|
|
+ */
|
|
|
+ RPC_Terminate_Orphan,
|
|
|
+
|
|
|
+ /* Allow an orphaned activation to continue executing.
|
|
|
+ */
|
|
|
+ RPC_Continue_Orphan,
|
|
|
+
|
|
|
+ /* Empty thread activation (No thread linked to it)
|
|
|
+ */
|
|
|
+ Not_Supported,
|
|
|
+
|
|
|
+ /* Remote node down or inaccessible.
|
|
|
+ */
|
|
|
+ Node_Down,
|
|
|
+
|
|
|
+ /* A signalled thread was not actually waiting. */
|
|
|
+ Not_Waiting,
|
|
|
+
|
|
|
+ /* Some thread-oriented operation (semaphore_wait) timed out
|
|
|
+ */
|
|
|
+ Operation_Timed_Out,
|
|
|
+
|
|
|
+ /* During a page fault, indicates that the page was rejected
|
|
|
+ * as a result of a signature check.
|
|
|
+ */
|
|
|
+ Codesign_Error,
|
|
|
+
|
|
|
+ /* The requested property cannot be changed at this time.
|
|
|
+ */
|
|
|
+ Policy_Static,
|
|
|
+
|
|
|
+ /* The provided buffer is of insufficient size for the requested data.
|
|
|
+ */
|
|
|
+ Insufficient_Buffer_Size,
|
|
|
+
|
|
|
+ /* Denied by security policy
|
|
|
+ */
|
|
|
+ Denied,
|
|
|
+
|
|
|
+ /* The KC on which the function is operating is missing
|
|
|
+ */
|
|
|
+ Missing_KC,
|
|
|
+
|
|
|
+ /* The KC on which the function is operating is invalid
|
|
|
+ */
|
|
|
+ Invalid_KC,
|
|
|
+
|
|
|
+ /* A search or query operation did not return a result
|
|
|
+ */
|
|
|
+ Not_Found,
|
|
|
+
|
|
|
+ /* Maximum return value allowable
|
|
|
+ */
|
|
|
+ Return_Max = 0x100,
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+ * VM allocation flags:
|
|
|
+ *
|
|
|
+ * VM_FLAGS_FIXED
|
|
|
+ * (really the absence of VM_FLAGS_ANYWHERE)
|
|
|
+ * Allocate new VM region at the specified virtual address, if possible.
|
|
|
+ *
|
|
|
+ * VM_FLAGS_ANYWHERE
|
|
|
+ * Allocate new VM region anywhere it would fit in the address space.
|
|
|
+ *
|
|
|
+ * VM_FLAGS_PURGABLE
|
|
|
+ * Create a purgable VM object for that new VM region.
|
|
|
+ *
|
|
|
+ * VM_FLAGS_4GB_CHUNK
|
|
|
+ * The new VM region will be chunked up into 4GB sized pieces.
|
|
|
+ *
|
|
|
+ * VM_FLAGS_NO_PMAP_CHECK
|
|
|
+ * (for DEBUG kernel config only, ignored for other configs)
|
|
|
+ * Do not check that there is no stale pmap mapping for the new VM region.
|
|
|
+ * This is useful for kernel memory allocations at bootstrap when building
|
|
|
+ * the initial kernel address space while some memory is already in use.
|
|
|
+ *
|
|
|
+ * VM_FLAGS_OVERWRITE
|
|
|
+ * The new VM region can replace existing VM regions if necessary
|
|
|
+ * (to be used in combination with VM_FLAGS_FIXED).
|
|
|
+ *
|
|
|
+ * VM_FLAGS_NO_CACHE
|
|
|
+ * Pages brought in to this VM region are placed on the speculative
|
|
|
+ * queue instead of the active queue. In other words, they are not
|
|
|
+ * cached so that they will be stolen first if memory runs low.
|
|
|
+ */
|
|
|
+
|
|
|
+@(private="file")
|
|
|
+LOG2 :: intrinsics.constant_log2
|
|
|
+
|
|
|
+VM_Flag :: enum c.int {
|
|
|
+ Anywhere,
|
|
|
+ Purgable,
|
|
|
+ _4GB_Chunk,
|
|
|
+ Random_Addr,
|
|
|
+ No_Cache,
|
|
|
+ Resilient_Codesign,
|
|
|
+ Resilient_Media,
|
|
|
+ Permanent,
|
|
|
+
|
|
|
+ // NOTE(beau): log 2 of the bit we want in the bit set so we get that bit in
|
|
|
+ // the bit set
|
|
|
+
|
|
|
+ TPRO = LOG2(0x1000),
|
|
|
+ Overwrite = LOG2(0x4000),/* delete any existing mappings first */
|
|
|
+
|
|
|
+ Superpage_Size_Any = LOG2(0x10000),
|
|
|
+ Superpage_Size_2MB = LOG2(0x20000),
|
|
|
+ __Superpage3 = LOG2(0x40000),
|
|
|
+
|
|
|
+ Return_Data_Addr = LOG2(0x100000),
|
|
|
+ Return_4K_Data_Addr = LOG2(0x800000),
|
|
|
+
|
|
|
+ Alias_Mask1 = 24,
|
|
|
+ Alias_Mask2,
|
|
|
+ Alias_Mask3,
|
|
|
+ Alias_Mask4,
|
|
|
+ Alias_Mask5,
|
|
|
+ Alias_Mask6,
|
|
|
+ Alias_Mask7,
|
|
|
+ Alias_Mask8,
|
|
|
+}
|
|
|
+
|
|
|
+VM_Flags :: distinct bit_set[VM_Flag; c.int]
|
|
|
+VM_FLAGS_FIXED :: VM_Flags{}
|
|
|
+
|
|
|
+/*
|
|
|
+ * VM_FLAGS_SUPERPAGE_MASK
|
|
|
+ * 3 bits that specify whether large pages should be used instead of
|
|
|
+ * base pages (!=0), as well as the requested page size.
|
|
|
+ */
|
|
|
+VM_FLAGS_SUPERPAGE_MASK :: VM_Flags {
|
|
|
+ .Superpage_Size_Any,
|
|
|
+ .Superpage_Size_2MB,
|
|
|
+ .__Superpage3,
|
|
|
+}
|
|
|
+
|
|
|
+// 0xFF000000
|
|
|
+VM_FLAGS_ALIAS_MASK :: VM_Flags {
|
|
|
+ .Alias_Mask1,
|
|
|
+ .Alias_Mask2,
|
|
|
+ .Alias_Mask3,
|
|
|
+ .Alias_Mask4,
|
|
|
+ .Alias_Mask5,
|
|
|
+ .Alias_Mask6,
|
|
|
+ .Alias_Mask7,
|
|
|
+ .Alias_Mask8,
|
|
|
+}
|
|
|
+
|
|
|
+VM_GET_FLAGS_ALIAS :: proc(flags: VM_Flags) -> c.int {
|
|
|
+ return transmute(c.int)(flags & VM_FLAGS_ALIAS_MASK) >> 24
|
|
|
+}
|
|
|
+// NOTE(beau): no need for VM_SET_FLAGS_ALIAS, just mask in things from
|
|
|
+// VM_Flag.Alias_Mask*
|
|
|
+
|
|
|
+VM_FLAG_HW :: VM_Flag.TPRO
|
|
|
+VM_FLAGS_HW :: VM_Flags{VM_FLAG_HW}
|
|
|
+
|
|
|
+/* These are the flags that we accept from user-space */
|
|
|
+VM_FLAGS_USER_ALLOCATE :: VM_Flags {
|
|
|
+ .Anywhere,
|
|
|
+ .Purgable,
|
|
|
+ ._4GB_Chunk,
|
|
|
+ .Random_Addr,
|
|
|
+ .No_Cache,
|
|
|
+ .Permanent,
|
|
|
+ .Overwrite,
|
|
|
+} | VM_FLAGS_FIXED | VM_FLAGS_SUPERPAGE_MASK | VM_FLAGS_ALIAS_MASK | VM_FLAGS_HW
|
|
|
+
|
|
|
+VM_FLAGS_USER_MAP :: VM_Flags {
|
|
|
+ .Return_4K_Data_Addr,
|
|
|
+ .Return_Data_Addr,
|
|
|
+} | VM_FLAGS_USER_ALLOCATE
|
|
|
+
|
|
|
+VM_FLAGS_USER_REMAP :: VM_Flags {
|
|
|
+ .Anywhere,
|
|
|
+ .Random_Addr,
|
|
|
+ .Overwrite,
|
|
|
+ .Return_Data_Addr,
|
|
|
+ .Resilient_Codesign,
|
|
|
+ .Resilient_Media,
|
|
|
+} | VM_FLAGS_FIXED
|
|
|
+
|
|
|
+VM_FLAGS_SUPERPAGE_NONE :: VM_Flags{} /* no superpages, if all bits are 0 */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Protection values, defined as bits within the vm_prot_t type
|
|
|
+ */
|
|
|
+
|
|
|
+VM_Prot :: enum vm_prot_t {
|
|
|
+ Read,
|
|
|
+ Write,
|
|
|
+ Execute,
|
|
|
+}
|
|
|
+
|
|
|
+VM_Prot_Flags :: distinct bit_set[VM_Prot; vm_prot_t]
|
|
|
+
|
|
|
+VM_PROT_NONE :: VM_Prot_Flags{}
|
|
|
+VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write}
|
|
|
+VM_PROT_ALL :: VM_Prot_Flags{.Read, .Write, .Execute}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Enumeration of valid values for vm_inherit_t.
|
|
|
+ */
|
|
|
+
|
|
|
+VM_Inherit :: enum vm_inherit_t {
|
|
|
+ Share,
|
|
|
+ Copy,
|
|
|
+ None,
|
|
|
+ Donate_Copy,
|
|
|
+}
|
|
|
+
|
|
|
+VM_INHERIT_DEFAULT :: VM_Inherit.Copy
|
|
|
+VM_INHERIT_LAST_VALID :: VM_Inherit.None
|
|
|
+
|
|
|
+Sync_Policy :: enum sync_policy_t {
|
|
|
+ Fifo,
|
|
|
+ Fixed_Priority,
|
|
|
+ Reversed,
|
|
|
+ Order_Mask,
|
|
|
+}
|
|
|
+
|
|
|
+SYNC_POLICY_LIFO :: Sync_Policy.Fifo | Sync_Policy.Reversed
|