[runtime] Remove explicit dependency on syscall/zx

The runtime dependency on syscall/zx makes it difficult to use
syscall/zx without causing import loops. This is also the first step in
consolidating the VDSO "syscall" entrypoint duplication between runtime
and syscall/zx.

Change-Id: I8252bc426cf2be7df7cce72ed1e1fa1984e85ca4
diff --git a/src/runtime/mem_fuchsia.go b/src/runtime/mem_fuchsia.go
index eee26bc..bb91252 100644
--- a/src/runtime/mem_fuchsia.go
+++ b/src/runtime/mem_fuchsia.go
@@ -5,14 +5,42 @@
 package runtime
 
 import (
-	"syscall/zx"
 	"unsafe"
 )
 
+const (
+	permRead  = 1 << 0
+	permWrite = 1 << 1
+
+	propName = 3
+)
+
+//go:linkname sys_vmar_unmap syscall/zx.Sys_vmar_unmap
+func sys_vmar_unmap(h uint32, addr unsafe.Pointer, l uint64) int32
+
+//go:linkname sys_vmo_create syscall/zx.Sys_vmo_create
+//go:noescape
+func sys_vmo_create(size uint64, options uint32, out *uint32) int32
+
+//go:linkname sys_object_set_property syscall/zx.Sys_object_set_property
+//go:noescape
+func sys_object_set_property(handle uint32, property uint32, value unsafe.Pointer, value_size uint) int32
+
+//go:linkname sys_vmar_map syscall/zx.Sys_vmar_map
+//go:noescape
+func sys_vmar_map(handle uint32, options uint32, vmar_offset uint64, vmo uint32, vmo_offset uint64, len uint64, mapped_addr *unsafe.Pointer) int32
+
+//go:linkname sys_handle_close syscall/zx.Sys_handle_close
+//go:noescape
+func sys_handle_close(handle uint32) int32
+
+//go:linkname sys_vmar_protect syscall/zx.Sys_vmar_protect
+func sys_vmar_protect(handle uint32, options uint32, addr unsafe.Pointer, len uint64) int32
+
 //go:nosplit
 func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer {
 	var p unsafe.Pointer
-	sysMmap(unsafe.Pointer(&p), n, zx.VMFlagPermRead|zx.VMFlagPermWrite)
+	sysMmap(unsafe.Pointer(&p), n, permRead|permWrite)
 	if uintptr(p) < 4096 {
 		println("runtime: sysMmap failed n=", n)
 		exit(2)
@@ -32,31 +60,31 @@
 //go:nosplit
 func sysFree(v unsafe.Pointer, n uintptr, sysStat *uint64) {
 	mSysStatDec(sysStat, n)
-	if status := zx.Sys_vmar_unmap(zx.Handle(zx.VMARRoot), zx.Vaddr(v), uint64(n)); status != zx.ErrOk {
-		println("runtime: zx.Sys_vmar_unmap failed: ", status.String())
+	if status := sys_vmar_unmap(fdioHandles.vmarRootSelf, v, uint64(n)); status != 0 {
+		println("runtime: sys_vmar_unmap failed: ", status)
 	}
 }
 
-func sysMmap(ptr unsafe.Pointer, sz uintptr, flags zx.VMFlag) {
-	var h zx.Handle
-	if status := zx.Sys_vmo_create(uint64(sz), 0, &h); status != zx.ErrOk {
-		println("runtime: zx.Sys_vmo_create failed: ", status.String())
+func sysMmap(ptr unsafe.Pointer, sz uintptr, flags uint32) {
+	var h uint32
+	if status := sys_vmo_create(uint64(sz), 0, &h); status != 0 {
+		println("runtime: sys_vmo_create failed: ", status)
 		exit(2)
 	}
 
 	prop := []byte("go-sys-mmap")
-	if status := zx.Sys_object_set_property(h, zx.PropName, unsafe.Pointer(&prop[0]), uint(len(prop))); status != zx.ErrOk {
-		println("runtime: zx.Sys_object_set_property failed: ", status.String())
+	if status := sys_object_set_property(h, propName, unsafe.Pointer(&prop[0]), uint(len(prop))); status != 0 {
+		println("runtime: sys_object_set_property failed: ", status)
 		exit(2)
 	}
 
-	if status := zx.Sys_vmar_map(zx.Handle(zx.VMARRoot), flags, 0, h, 0, uint64(sz), (*zx.Vaddr)(ptr)); status != zx.ErrOk {
-		println("runtime: zx.Sys_vmar_map failed: ", status.String())
+	if status := sys_vmar_map(fdioHandles.vmarRootSelf, flags, 0, h, 0, uint64(sz), (*unsafe.Pointer)(ptr)); status != 0 {
+		println("runtime: sys_vmar_map failed: ", status)
 		exit(2)
 	}
 
-	if status := zx.Sys_handle_close(h); status != zx.ErrOk {
-		println("runtime: zx.Sys_handle_close failed: ", status.String())
+	if status := sys_handle_close(h); status != 0 {
+		println("runtime: sys_handle_close failed: ", status)
 		exit(2)
 	}
 }
@@ -74,8 +102,8 @@
 func sysMap(v unsafe.Pointer, n uintptr, sysStat *uint64) {
 	mSysStatInc(sysStat, n)
 
-	if status := zx.Sys_vmar_protect(zx.Handle(zx.VMARRoot), zx.VMFlagPermRead|zx.VMFlagPermWrite, zx.Vaddr(v), uint64(n)); status != zx.ErrOk {
-		println("runtime: zx.Sys_vmar_protect failed: ", status.String())
+	if status := sys_vmar_protect(fdioHandles.vmarRootSelf, permRead|permWrite, v, uint64(n)); status != 0 {
+		println("runtime: sys_vmar_protect failed: ", status)
 		exit(2)
 	}
 }
diff --git a/src/runtime/os_fuchsia.go b/src/runtime/os_fuchsia.go
index a640020..219fd53 100644
--- a/src/runtime/os_fuchsia.go
+++ b/src/runtime/os_fuchsia.go
@@ -6,14 +6,73 @@
 
 import (
 	"runtime/internal/atomic"
-	"syscall/zx"
 	"unsafe"
 )
 
-//go:nosplit
-func cputicks() int64 {
-	return int64(zx.Sys_ticks_get())
-}
+const nsecInf = int64(0x7FFFFFFFFFFFFFFF)
+
+func osyield()
+
+//go:linkname cputicks syscall/zx.Sys_ticks_get
+func cputicks() int64
+
+//go:linkname sys_cprng_draw syscall/zx.Sys_cprng_draw
+//go:noescape
+func sys_cprng_draw(buffer unsafe.Pointer, size uint)
+
+//go:linkname sys_system_get_num_cpus syscall/zx.Sys_system_get_num_cpus
+func sys_system_get_num_cpus() uint32
+
+//go:linkname sys_deadline_after syscall/zx.Sys_deadline_after
+func sys_deadline_after(ns int64) int64
+
+//go:linkname sys_futex_wait syscall/zx.Sys_futex_wait
+//go:noescape
+func sys_futex_wait(v *int32, cv int32, newOwner uint32, deadline int64) int32
+
+//go:linkname sys_futex_wake syscall/zx.Sys_futex_wake
+//go:noescape
+func sys_futex_wake(v *int32, count uint32) int32
+
+//go:linkname sys_thread_create syscall/zx.Sys_thread_create
+//go:noescape
+func sys_thread_create(p uint32, name *byte, nlen uint, opts int, out *uint32) int32
+
+//go:linkname sys_thread_start syscall/zx.Sys_thread_start
+//go:noescape
+func sys_thread_start(h uint32, pc unsafe.Pointer, stk unsafe.Pointer, mp uintptr, arg2 uintptr) int32
+
+//go:linkname sys_clock_get_monotonic syscall/zx.Sys_clock_get_monotonic
+func sys_clock_get_monotonic() int64
+
+//go:linkname sys_clock_get syscall/zx.Sys_clock_get
+//go:noescape
+func sys_clock_get(clock_id uint32, out *int64) int32
+
+//go:linkname sys_debuglog_create syscall/zx.Sys_debuglog_create
+//go:noescape
+func sys_debuglog_create(resource uint32, options uint32, out *uint32) int32
+
+//go:linkname sys_debuglog_write syscall/zx.Sys_debuglog_write
+func sys_debuglog_write(handle uint32, options uint32, buffer unsafe.Pointer, buffer_size uint) int32
+
+//go:linkname sys_nanosleep syscall/zx.Sys_nanosleep
+func sys_nanosleep(deadline int64) int32
+
+//go:linkname sys_process_exit syscall/zx.Sys_process_exit
+func sys_process_exit(retcode int64)
+
+//go:linkname zx_set_stdio_handle syscall/zx.set_stdio_handle
+func zx_set_stdio_handle(n int, h uint32, t uint32)
+
+//go:linkname zx_set_proc_handle syscall/zx.set_proc_handle
+func zx_set_proc_handle(h uint32)
+
+//go:linkname zx_set_vmar_root syscall/zx.set_vmar_root
+func zx_set_vmar_root(h uint32)
+
+//go:linkname zx_set_namespace syscall/zx.set_namespace
+func zx_set_namespace(m map[string]uint32)
 
 type mOS struct{}
 
@@ -21,26 +80,24 @@
 
 //go:nosplit
 func getRandomData(r []byte) {
-	zx.RandRead(r)
+	if len(r) == 0 {
+		return
+	}
+	sys_cprng_draw(unsafe.Pointer(&r[0]), uint(len(r)))
 }
 
 //go:nosplit
 func futexsleep(addr *uint32, val uint32, ns int64) {
-	deadline := zx.Sys_deadline_after(zx.Duration(ns))
+	deadline := sys_deadline_after(ns)
 	if ns < 0 {
-		deadline = zx.Time(zx.TimensecInfinite)
+		deadline = nsecInf
 	}
-	zx.Sys_futex_wait((*int32)(unsafe.Pointer(addr)), int32(val), zx.HandleInvalid, deadline)
+	sys_futex_wait((*int32)(unsafe.Pointer(addr)), int32(val), 0, deadline)
 }
 
 //go:nosplit
 func futexwakeup(addr *uint32, cnt uint32) {
-	zx.Sys_futex_wake((*int32)(unsafe.Pointer(addr)), cnt)
-}
-
-//go:nosplit
-func osyield() {
-	zx.Sys_nanosleep(0)
+	sys_futex_wake((*int32)(unsafe.Pointer(addr)), cnt)
 }
 
 // cgocallm0 calls a C function on the current stack.
@@ -55,18 +112,16 @@
 	if _cgo_get_initial_handles != nil {
 		cgocallm0(_cgo_get_initial_handles, unsafe.Pointer(&fdioHandles))
 		for i := 0; i < 3; i = i + 1 {
-			zx.StdioHandles[i] = fdioHandles.stdioClones[i]
-			zx.StdioHandleTypes[i] = int(fdioHandles.stdioCloneTypes[i])
+			zx_set_stdio_handle(i, fdioHandles.stdioClones[i], fdioHandles.stdioCloneTypes[i])
 		}
-		zx.ProcHandle = zx.Handle(fdioHandles.processSelf)
-		zx.VMARRoot = zx.VMAR(fdioHandles.vmarRootSelf)
+		zx_set_proc_handle(fdioHandles.processSelf)
+		zx_set_vmar_root(fdioHandles.vmarRootSelf)
 	} else {
-		// TODO: implement cgo-less init
-		println("runtime: no fuchsia process handle without cgo yet")
+		println("runtime: fuchsia requires cgo")
 		exit(2)
 	}
 
-	ncpu = int32(zx.Sys_system_get_num_cpus())
+	ncpu = int32(sys_system_get_num_cpus())
 	physPageSize = 4096
 }
 
@@ -74,16 +129,17 @@
 	if fdioHandles.rootNSNumHandles > 0 {
 		const maxHandleCount = 1 << 20 // arbitrary
 		paths := (*(*[maxHandleCount]*byte)(unsafe.Pointer(fdioHandles.rootNSPaths)))[:fdioHandles.rootNSNumHandles]
-		handles := (*(*[maxHandleCount]zx.Handle)(unsafe.Pointer(fdioHandles.rootNSHandles)))[:fdioHandles.rootNSNumHandles]
-		zx.RootNSMap = make(map[string]zx.Handle)
+		handles := (*(*[maxHandleCount]uint32)(unsafe.Pointer(fdioHandles.rootNSHandles)))[:fdioHandles.rootNSNumHandles]
+		m := make(map[string]uint32)
 		for i, p := range paths {
-			zx.RootNSMap[gostring(p)] = handles[i]
+			m[gostring(p)] = handles[i]
 		}
+
+		zx_set_namespace(m)
 	}
 }
 
 // Filled in by runtime/cgo when linked into binary.
-
 var (
 	_cgo_get_initial_handles    unsafe.Pointer
 	_cgo_get_thread_self_handle unsafe.Pointer
@@ -91,7 +147,7 @@
 
 //go:nosplit
 func minit() {
-	var h zx.Handle
+	var h uint32
 	asmcgocall(_cgo_get_thread_self_handle, unsafe.Pointer(&h))
 	atomic.Storeuintptr(&getg().m.thread, uintptr(h))
 }
@@ -151,19 +207,19 @@
 //go:nowritebarrier
 func newosproc(mp *m) {
 	stk := unsafe.Pointer(mp.g0.stack.hi)
-	p := zx.Handle(0) // TODO: only works due to temporary hack in zircon
-	var h zx.Handle
-	status := zx.Sys_thread_create(p, &gothreadname[0], uint(len(gothreadname)), 0, &h)
+	p := uint32(0) // TODO: only works due to temporary hack in zircon
+	var h uint32
+	status := sys_thread_create(p, &gothreadname[0], uint(len(gothreadname)), 0, &h)
 	if status < 0 {
-		println("runtime: newosproc zx.Sys_thread_create failed:", h)
+		println("runtime: newosproc sys_thread_create failed:", h)
 		exit(2)
 	}
 
 	mp.thread = uintptr(h)
 
-	status = zx.Sys_thread_start(h, zx.Vaddr(funcPC(threadinit)), zx.Vaddr(stk), uintptr(unsafe.Pointer(mp)), 0)
+	status = sys_thread_start(h, unsafe.Pointer(funcPC(threadinit)), stk, uintptr(unsafe.Pointer(mp)), 0)
 	if status < 0 {
-		println("runtime: newosproc zx.Sys_thread_start failed:", h)
+		println("runtime: newosproc sys_thread_start failed:", h)
 		exit(2)
 	}
 }
@@ -181,7 +237,7 @@
 	version       uint32
 	next_tls_slot uint32
 	proc_args     *zx_proc_args
-	handle        *zx.Handle
+	handle        *uint32
 	handle_info   *uint32
 	handle_count  int32
 	argv          **byte
@@ -203,16 +259,16 @@
 
 // fdio_init matches a Go struct of the same name in gcc_fdio.c.
 type fdio_init struct {
-	stdioClones          [3]zx.Handle
+	stdioClones          [3]uint32
 	stdioCloneNumHandles [3]uint32
 	stdioCloneTypes      [3]uint32
-	processSelf          zx.Handle
-	vmarRootSelf         zx.Handle
+	processSelf          uint32
+	vmarRootSelf         uint32
 	envlen               int32
 	environ              **byte
 	_                    [4]byte
 	rootNSNumHandles     int32
-	rootNSHandles        *zx.Handle
+	rootNSHandles        *uint32
 	rootNSPaths          **byte
 }
 
@@ -253,15 +309,15 @@
 
 //go:nosplit
 func nanotime() int64 {
-	return int64(zx.Sys_clock_get_monotonic())
+	return sys_clock_get_monotonic()
 }
 
 //go:linkname time_now time.now
 //go:nosplit
 func time_now() (sec int64, nsec int32, mono int64) {
 	const ZX_CLOCK_UTC = 1
-	var x zx.Time
-	zx.Sys_clock_get(ZX_CLOCK_UTC, &x)
+	var x int64
+	sys_clock_get(ZX_CLOCK_UTC, &x)
 	return int64(x / 1e9), int32(x % 1e9), nanotime()
 }
 
@@ -272,7 +328,7 @@
 // #define LOGBUF_MAX (ZX_LOG_RECORD_MAX - sizeof(zx_log_record_t))
 const logBufMax = 224
 
-var logger zx.Handle
+var logger uint32
 var logBufferArray [logBufMax]byte
 var logBufferN int
 
@@ -286,7 +342,7 @@
 	// these file descriptors come from print, they will already be serialized by a lock in
 	// print's implementation.
 	if logger <= 0 {
-		zx.Sys_debuglog_create(0, 0, &logger)
+		sys_debuglog_create(0, 0, &logger)
 		logBufferN = 0
 	}
 	// Ideally this should panic, but that would go down "blind", which is arguably worse.
@@ -298,7 +354,7 @@
 		c := *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i)))
 
 		if c == '\n' {
-			zx.Sys_debuglog_write(logger, 0, unsafe.Pointer(&logBufferArray[0]), uint(logBufferN))
+			sys_debuglog_write(logger, 0, unsafe.Pointer(&logBufferArray[0]), uint(logBufferN))
 			logBufferN = 0
 			continue
 		}
@@ -311,7 +367,7 @@
 		logBufferN++
 
 		if logBufferN == len(logBufferArray) {
-			zx.Sys_debuglog_write(logger, 0, unsafe.Pointer(&logBufferArray[0]), uint(logBufferN))
+			sys_debuglog_write(logger, 0, unsafe.Pointer(&logBufferArray[0]), uint(logBufferN))
 			logBufferN = 0
 		}
 	}
@@ -321,12 +377,12 @@
 
 //go:nosplit
 func exit(code int32) {
-	zx.Sys_process_exit(int64(code))
+	sys_process_exit(int64(code))
 }
 
 //go:nosplit
 func usleep(usec uint32) {
-	zx.Sys_nanosleep(zx.Sys_deadline_after(zx.Duration(usec * 1000)))
+	sys_nanosleep(sys_deadline_after(int64(usec * 1000)))
 }
 
 func signame(sig uint32) string {
diff --git a/src/runtime/sys_fuchsia_amd64.s b/src/runtime/sys_fuchsia_amd64.s
index e909874..ef752c5 100644
--- a/src/runtime/sys_fuchsia_amd64.s
+++ b/src/runtime/sys_fuchsia_amd64.s
@@ -85,3 +85,9 @@
 	CALL AX
 	MOVL AX, ret+24(FP)
 	RET
+
+TEXT runtime·osyield(SB),NOSPLIT,$0
+	MOVQ $0, DI
+	MOVQ vdso_zx_nanosleep(SB), AX
+	CALL AX
+	RET
diff --git a/src/runtime/sys_fuchsia_arm64.s b/src/runtime/sys_fuchsia_arm64.s
index a391e7d..5055bc3 100644
--- a/src/runtime/sys_fuchsia_arm64.s
+++ b/src/runtime/sys_fuchsia_arm64.s
@@ -51,3 +51,8 @@
 	BL vdso_zx_object_wait_one(SB)
 	MOVW R0, ret+24(FP)
 	RET
+
+TEXT runtime·osyield(SB),NOSPLIT,$0
+	MOVD $0, R0
+	BL vdso_zx_nanosleep(SB)
+	RET
diff --git a/src/syscall/zx/handle.go b/src/syscall/zx/handle.go
index 66bc050..28e2005 100644
--- a/src/syscall/zx/handle.go
+++ b/src/syscall/zx/handle.go
@@ -25,6 +25,29 @@
 
 type HandleInfo uint32
 
+func set_stdio_handle(n int, h uint32, t uint32) {
+	StdioHandles[n] = Handle(h)
+	StdioHandleTypes[n] = int(t)
+}
+
+func set_proc_handle(h uint32) {
+	ProcHandle = Handle(h)
+}
+
+func set_vmar_root(h uint32) {
+	VMARRoot = VMAR(h)
+}
+
+func set_namespace(ns map[string]uint32) {
+	if RootNSMap == nil {
+		RootNSMap = make(map[string]Handle)
+	}
+
+	for k, v := range ns {
+		RootNSMap[k] = Handle(v)
+	}
+}
+
 func NewHandleInfo(t, arg uint32) HandleInfo {
 	return HandleInfo((t & 0xFFFF) | ((arg & 0xFFFF) << 16))
 }