[libc] implement CLOCK_BOOTTIME

CLOCK_BOOTTIME is defined, but was not implemented. This lead to issues in
openssh-portable, which was falling back to gettimeofday due to the EINVAL it
received from clock_gettime.

Test: system/test/c/clock-gettime
Bug: 38552
Change-Id: I811b0f5dd32629e4945c5021e75a719a4c5981ba
diff --git a/zircon/system/ulib/c/test/BUILD.gn b/zircon/system/ulib/c/test/BUILD.gn
index 0c7104e..f87f4ce 100644
--- a/zircon/system/ulib/c/test/BUILD.gn
+++ b/zircon/system/ulib/c/test/BUILD.gn
@@ -117,9 +117,21 @@
   ]
 }
 
+test("clock-gettime") {
+  test_group = "c"
+  sources = [
+    "clock-gettime.cc",
+  ]
+  deps = [
+    "$zx/system/ulib/fdio",
+    "$zx/system/ulib/zx",
+  ]
+}
+
 group("test") {
   testonly = true
   deps = [
+    ":clock-gettime",
     ":debugdata",
     ":noop-tests.instrumented",
   ]
diff --git a/zircon/system/ulib/c/test/clock-gettime.cc b/zircon/system/ulib/c/test/clock-gettime.cc
new file mode 100644
index 0000000..af58a4b
--- /dev/null
+++ b/zircon/system/ulib/c/test/clock-gettime.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <time.h>
+#include <zircon/assert.h>
+
+void test_monotonics() {
+  // The test strategy here is limited, as we do not have a straightforward
+  // mechanism with which to modify the underlying syscall behavior. We switch
+  // back and forward between calling clock_gettime with CLOCK_MONOTONIC and
+  // CLOCK_BOOTTIME, and assert their relative monotonicity. This test ensures
+  // that these calls succeed, and that time is at least frozen, if not
+  // increasing in a monotonic fashion, with repect to both clock ids.
+
+  struct timespec ts;
+
+  int which = 0;
+  for (int i = 0; i < 100; i++) {
+    struct timespec last = ts;
+
+    switch (which) {
+    case 0:
+      ZX_ASSERT(0 == clock_gettime(CLOCK_MONOTONIC, &ts));
+      break;
+    case 1:
+      ZX_ASSERT(0 == clock_gettime(CLOCK_BOOTTIME, &ts));
+      break;
+    case 2:
+      ZX_ASSERT(0 == clock_gettime(CLOCK_MONOTONIC_RAW, &ts));
+      break;
+    }
+
+    if (ts.tv_sec == last.tv_sec) {
+      ZX_ASSERT_MSG(
+        ts.tv_nsec >= last.tv_nsec,
+        "clock_gettime(CLOCK_{MONOTONIC,BOOTTIME}): %ld < %ld",
+        ts.tv_nsec, last.tv_nsec
+      );
+    } else {
+      ZX_ASSERT_MSG(
+        ts.tv_sec >= last.tv_sec,
+        "clock_gettime(CLOCK_{MONOTONIC,BOOTTIME}): %ld (current) < %ld (last)",
+        ts.tv_sec, last.tv_sec
+      );
+    }
+
+    if (++which % 3 == 0) {
+      which = 0;
+    }
+  }
+}
+
+int main() {
+  test_monotonics();
+  return 0;
+}
diff --git a/zircon/third_party/ulib/musl/src/time/clock_gettime.c b/zircon/third_party/ulib/musl/src/time/clock_gettime.c
index 6d42cc7..882df65 100644
--- a/zircon/third_party/ulib/musl/src/time/clock_gettime.c
+++ b/zircon/third_party/ulib/musl/src/time/clock_gettime.c
@@ -9,6 +9,7 @@
 int __clock_gettime(clockid_t clk, struct timespec* ts) {
   uint32_t zx_clock;
   switch (clk) {
+    case CLOCK_BOOTTIME: // see fxb/38552
     case CLOCK_MONOTONIC:
     case CLOCK_MONOTONIC_RAW:
       zx_clock = ZX_CLOCK_MONOTONIC;