diff --git a/pkg/sentry/time/BUILD b/pkg/sentry/time/BUILD index c21971322..973577cfc 100644 --- a/pkg/sentry/time/BUILD +++ b/pkg/sentry/time/BUILD @@ -27,10 +27,12 @@ go_library( "sampler.go", "sampler_amd64.go", "sampler_arm64.go", - "sampler_unsafe.go", "seqatomic_parameters_unsafe.go", "tsc_amd64.s", "tsc_arm64.s", + "vdso.go", + "vdso_amd64.s", + "vdso_arm64.s", ], visibility = ["//:sandbox"], deps = [ @@ -49,6 +51,11 @@ go_test( "calibrated_clock_test.go", "parameters_test.go", "sampler_test.go", + "vdso_test.go", ], library = ":time", + deps = [ + "//pkg/abi/linux", + "@org_golang_x_sys//unix:go_default_library", + ], ) diff --git a/pkg/sentry/time/sampler.go b/pkg/sentry/time/sampler.go index 24a47f5d5..1b27c3786 100644 --- a/pkg/sentry/time/sampler.go +++ b/pkg/sentry/time/sampler.go @@ -17,6 +17,7 @@ package time import ( "errors" + "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/log" ) @@ -216,3 +217,35 @@ func (s *sampler) Range() (sample, sample, bool) { return s.samples[0], s.samples[len(s.samples)-1], true } + +// syscallTSCReferenceClocks is the standard referenceClocks, collecting +// samples using CLOCK_GETTIME and RDTSC. +type syscallTSCReferenceClocks struct { + tscCycleClock +} + +// Sample implements sampler.Sample. +func (syscallTSCReferenceClocks) Sample(c ClockID) (sample, error) { + var s sample + + s.before = Rdtsc() + + // Don't call clockGettime to avoid a call which may call morestack. + var ts unix.Timespec + + vdsoClockGettime(c, &ts) + + s.after = Rdtsc() + s.ref = ReferenceNS(ts.Nano()) + + return s, nil +} + +// clockGettime calls SYS_CLOCK_GETTIME, returning time in nanoseconds. +func clockGettime(c ClockID) (ReferenceNS, error) { + var ts unix.Timespec + + vdsoClockGettime(c, &ts) + + return ReferenceNS(ts.Nano()), nil +} diff --git a/pkg/sentry/time/sampler_unsafe.go b/pkg/sentry/time/sampler_unsafe.go deleted file mode 100644 index 2ab57c5f8..000000000 --- a/pkg/sentry/time/sampler_unsafe.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package time - -import ( - "unsafe" - - "golang.org/x/sys/unix" -) - -// syscallTSCReferenceClocks is the standard referenceClocks, collecting -// samples using CLOCK_GETTIME and RDTSC. -type syscallTSCReferenceClocks struct { - tscCycleClock -} - -// Sample implements sampler.Sample. -func (syscallTSCReferenceClocks) Sample(c ClockID) (sample, error) { - var s sample - - s.before = Rdtsc() - - // Don't call clockGettime to avoid a call which may call morestack. - var ts unix.Timespec - _, _, e := unix.RawSyscall(unix.SYS_CLOCK_GETTIME, uintptr(c), uintptr(unsafe.Pointer(&ts)), 0) - if e != 0 { - return sample{}, e - } - - s.after = Rdtsc() - s.ref = ReferenceNS(ts.Nano()) - - return s, nil -} - -// clockGettime calls SYS_CLOCK_GETTIME, returning time in nanoseconds. -func clockGettime(c ClockID) (ReferenceNS, error) { - var ts unix.Timespec - _, _, e := unix.RawSyscall(unix.SYS_CLOCK_GETTIME, uintptr(c), uintptr(unsafe.Pointer(&ts)), 0) - if e != 0 { - return 0, e - } - - return ReferenceNS(ts.Nano()), nil -} diff --git a/pkg/sentry/time/vdso.go b/pkg/sentry/time/vdso.go new file mode 100644 index 000000000..49bf2dfd2 --- /dev/null +++ b/pkg/sentry/time/vdso.go @@ -0,0 +1,21 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package time + +import ( + "golang.org/x/sys/unix" +) + +func vdsoClockGettime(clockid ClockID, ts *unix.Timespec) int diff --git a/pkg/sentry/time/vdso_amd64.s b/pkg/sentry/time/vdso_amd64.s new file mode 100644 index 000000000..95b6c0526 --- /dev/null +++ b/pkg/sentry/time/vdso_amd64.s @@ -0,0 +1,32 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "textflag.h" + +#define SYS_clock_gettime 228 + +TEXT ·vdsoClockGettime(SB), NOSPLIT, $0-24 + MOVL clockid+0(FP), DI + MOVQ ts+8(FP), SI + MOVQ runtime·vdsoClockgettimeSym(SB), AX + CMPQ AX, $0 + JEQ fallback + CALL AX + MOVQ AX, ret+16(FP) + RET +fallback: + MOVQ $SYS_clock_gettime, AX + SYSCALL + MOVQ AX, ret+16(FP) + RET diff --git a/pkg/sentry/time/vdso_arm64.s b/pkg/sentry/time/vdso_arm64.s new file mode 100644 index 000000000..b6cd209d1 --- /dev/null +++ b/pkg/sentry/time/vdso_arm64.s @@ -0,0 +1,31 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "textflag.h" + +#define SYS_clock_gettime 113 + +TEXT ·vdsoClockGettime(SB), NOSPLIT, $0-24 + MOVW clockid+0(FP), R0 + MOVD ts+8(FP), R1 + MOVD runtime·vdsoClockgettimeSym(SB), R2 + CBZ R2, fallback + BL (R2) + MOVD R0, ret+16(FP) + RET +fallback: + MOVD $SYS_clock_gettime, R8 + SVC + MOVD R0, ret+16(FP) + RET diff --git a/pkg/sentry/time/vdso_test.go b/pkg/sentry/time/vdso_test.go new file mode 100644 index 000000000..f935db7fd --- /dev/null +++ b/pkg/sentry/time/vdso_test.go @@ -0,0 +1,48 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package time + +import ( + "syscall" + "testing" + "unsafe" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/abi/linux" +) + +func TestClockGetTime(t *testing.T) { + ts := unix.Timespec{} + if ret := vdsoClockGettime(linux.CLOCK_MONOTONIC, &ts); ret != 0 { + t.Fatalf("Unexpected error code: %v", ret) + } + sts := unix.Timespec{} + if _, _, errno := unix.RawSyscall(unix.SYS_CLOCK_GETTIME, + uintptr(linux.CLOCK_MONOTONIC), + uintptr(unsafe.Pointer(&sts)), 0); errno != 0 { + t.Fatalf("Unexpected error code: %v", errno) + } + // Check that ts.Sec is in [sts.Sec, sts.Sec + 5]. + if sts.Sec < ts.Sec || sts.Sec > ts.Sec+5 { + t.Fatalf("Unexpected delta: vdso %+v syscall %+v", ts, sts) + } +} + +func TestClockGetTimeEINVAL(t *testing.T) { + ts := unix.Timespec{} + if ret := vdsoClockGettime(-1, &ts); ret != -int(syscall.EINVAL) { + t.Fatalf("Unexpected error code: %v", ret) + } +}