// Copyright 2018 The Crashpad 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 "snapshot/fuchsia/system_snapshot_fuchsia.h"

#include <zircon/syscalls.h>

#include "base/check_op.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "snapshot/posix/timezone.h"

namespace crashpad {
namespace internal {

SystemSnapshotFuchsia::SystemSnapshotFuchsia() = default;

SystemSnapshotFuchsia::~SystemSnapshotFuchsia() = default;

void SystemSnapshotFuchsia::Initialize(const timeval* snapshot_time) {
  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);

  snapshot_time_ = snapshot_time;

  // This version string mirrors `uname -a` as written by
  // garnet/bin/uname/uname.c, however, this information isn't provided by
  // uname(). Additionally, uname() seems to hang if the network is in a bad
  // state when attempting to retrieve the nodename, so avoid it for now.
  std::string kernel_version = zx_system_get_version_string();

#if defined(ARCH_CPU_X86_64)
  static constexpr const char kArch[] = "x86_64";
#elif defined(ARCH_CPU_ARM64)
  static constexpr const char kArch[] = "aarch64";
#else
  static constexpr const char kArch[] = "unknown";
#endif
  os_version_full_ = base::StringPrintf(
      "Zircon prerelease %s %s", kernel_version.c_str(), kArch);

  INITIALIZATION_STATE_SET_VALID(initialized_);
}

CPUArchitecture SystemSnapshotFuchsia::GetCPUArchitecture() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);

#if defined(ARCH_CPU_X86_64)
  return kCPUArchitectureX86_64;
#elif defined(ARCH_CPU_ARM64)
  return kCPUArchitectureARM64;
#else
#error Port
#endif
}

uint32_t SystemSnapshotFuchsia::CPURevision() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.Revision();
#else
  // TODO(fuchsia/DX-712): Read actual revision.
  return 0;
#endif
}

uint8_t SystemSnapshotFuchsia::CPUCount() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  return base::saturated_cast<uint8_t>(zx_system_get_num_cpus());
}

std::string SystemSnapshotFuchsia::CPUVendor() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.Vendor();
#else
  // TODO(fuchsia/DX-712): Read actual vendor.
  return std::string();
#endif
}

void SystemSnapshotFuchsia::CPUFrequency(uint64_t* current_hz,
                                         uint64_t* max_hz) const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  // TODO(scottmg): https://crashpad.chromium.org/bug/196.
  *current_hz = 0;
  *max_hz = 0;
}

uint32_t SystemSnapshotFuchsia::CPUX86Signature() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.Signature();
#else
  NOTREACHED();
  return 0;
#endif
}

uint64_t SystemSnapshotFuchsia::CPUX86Features() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.Features();
#else
  NOTREACHED();
  return 0;
#endif
}

uint64_t SystemSnapshotFuchsia::CPUX86ExtendedFeatures() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.ExtendedFeatures();
#else
  NOTREACHED();
  return 0;
#endif
}

uint32_t SystemSnapshotFuchsia::CPUX86Leaf7Features() const {
#if defined(ARCH_CPU_X86_64)
  return cpuid_.Leaf7Features();
#else
  NOTREACHED();
  return 0;
#endif
}

bool SystemSnapshotFuchsia::CPUX86SupportsDAZ() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.SupportsDAZ();
#else
  NOTREACHED();
  return false;
#endif
}

SystemSnapshot::OperatingSystem SystemSnapshotFuchsia::GetOperatingSystem()
    const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  return kOperatingSystemFuchsia;
}

bool SystemSnapshotFuchsia::OSServer() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  return false;
}

void SystemSnapshotFuchsia::OSVersion(int* major,
                                      int* minor,
                                      int* bugfix,
                                      std::string* build) const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  // TODO(scottmg): https://crashpad.chromium.org/bug/196. There's no version
  // available to be reported yet.
  *major = 0;
  *minor = 0;
  *bugfix = 0;
  *build = std::string();
}

std::string SystemSnapshotFuchsia::OSVersionFull() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  return os_version_full_;
}

std::string SystemSnapshotFuchsia::MachineDescription() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
  // TODO(scottmg): https://crashpad.chromium.org/bug/196. Not yet available,
  // upstream ZX-1775.
  return std::string();
}

bool SystemSnapshotFuchsia::NXEnabled() const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
  return cpuid_.NXEnabled();
#else
  // TODO(fuchsia/DX-712): Read actual NX bit value.
  return false;
#endif
}

void SystemSnapshotFuchsia::TimeZone(DaylightSavingTimeStatus* dst_status,
                                     int* standard_offset_seconds,
                                     int* daylight_offset_seconds,
                                     std::string* standard_name,
                                     std::string* daylight_name) const {
  INITIALIZATION_STATE_DCHECK_VALID(initialized_);

  internal::TimeZone(*snapshot_time_,
                     dst_status,
                     standard_offset_seconds,
                     daylight_offset_seconds,
                     standard_name,
                     daylight_name);
}

}  // namespace internal
}  // namespace crashpad
