1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Platform-specific code for Starboard goes here. Starboard is the platform
6 // abstraction layer for Cobalt, an HTML5 container used mainly by YouTube
7 // apps in the living room.
8 
9 #include "src/base/lazy-instance.h"
10 #include "src/base/macros.h"
11 #include "src/base/platform/platform.h"
12 #include "src/base/platform/time.h"
13 #include "src/base/timezone-cache.h"
14 #include "src/base/utils/random-number-generator.h"
15 #include "starboard/client_porting/eztime/eztime.h"
16 #include "starboard/common/condition_variable.h"
17 #include "starboard/common/log.h"
18 #include "starboard/common/string.h"
19 #include "starboard/configuration.h"
20 #include "starboard/configuration_constants.h"
21 #include "starboard/memory.h"
22 #include "starboard/time.h"
23 #include "starboard/time_zone.h"
24 
25 namespace v8 {
26 namespace base {
27 
28 #ifdef __arm__
ArmUsingHardFloat()29 bool OS::ArmUsingHardFloat() {
30   // GCC versions 4.6 and above define __ARM_PCS or __ARM_PCS_VFP to specify
31   // the Floating Point ABI used (PCS stands for Procedure Call Standard).
32   // We use these as well as a couple of other defines to statically determine
33   // what FP ABI used.
34   // GCC versions 4.4 and below don't support hard-fp.
35   // GCC versions 4.5 may support hard-fp without defining __ARM_PCS or
36   // __ARM_PCS_VFP.
37 
38 #define GCC_VERSION \
39   (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
40 #if GCC_VERSION >= 40600 && !defined(__clang__)
41 #if defined(__ARM_PCS_VFP)
42   return true;
43 #else
44   return false;
45 #endif
46 
47 #elif GCC_VERSION < 40500 && !defined(__clang__)
48   return false;
49 
50 #else
51 #if defined(__ARM_PCS_VFP)
52   return true;
53 #elif defined(__ARM_PCS) || defined(__SOFTFP__) || defined(__SOFTFP) || \
54     !defined(__VFP_FP__)
55   return false;
56 #else
57 #error \
58     "Your version of compiler does not report the FP ABI compiled for."     \
59        "Please report it on this issue"                                        \
60        "http://code.google.com/p/v8/issues/detail?id=2140"
61 
62 #endif
63 #endif
64 #undef GCC_VERSION
65 }
66 #endif  // def __arm__
67 
68 namespace {
69 
70 static LazyInstance<RandomNumberGenerator>::type
71     platform_random_number_generator = LAZY_INSTANCE_INITIALIZER;
72 static LazyMutex rng_mutex = LAZY_MUTEX_INITIALIZER;
73 
74 bool g_hard_abort = false;
75 // We only use this stack size to get the topmost stack frame.
76 const int kStackSize = 1;
77 
78 }  // namespace
79 
Initialize(bool hard_abort,const char * const gc_fake_mmap)80 void OS::Initialize(bool hard_abort, const char* const gc_fake_mmap) {
81   g_hard_abort = hard_abort;
82   // This is only used on Posix, we don't need to use it for anything.
83 }
84 
GetUserTime(uint32_t * secs,uint32_t * usecs)85 int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
86 #if SB_API_VERSION >= 12
87   if (!SbTimeIsTimeThreadNowSupported()) return -1;
88 #endif
89 
90 #if SB_API_VERSION >= 12 || SB_HAS(TIME_THREAD_NOW)
91   SbTimeMonotonic thread_now = SbTimeGetMonotonicThreadNow();
92   *secs = thread_now / kSbTimeSecond;
93   *usecs = thread_now % kSbTimeSecond;
94   return 0;
95 #else
96   return -1;
97 #endif
98 }
99 
TimeCurrentMillis()100 double OS::TimeCurrentMillis() { return Time::Now().ToJsTime(); }
101 
ActivationFrameAlignment()102 int OS::ActivationFrameAlignment() {
103 #if V8_TARGET_ARCH_ARM
104   // On EABI ARM targets this is required for fp correctness in the
105   // runtime system.
106   return 8;
107 #elif V8_TARGET_ARCH_MIPS
108   return 8;
109 #elif V8_TARGET_ARCH_S390
110   return 8;
111 #else
112   // Otherwise we just assume 16 byte alignment, i.e.:
113   // - With gcc 4.4 the tree vectorization optimizer can generate code
114   //   that requires 16 byte alignment such as movdqa on x86.
115   // - Mac OS X, PPC and Solaris (64-bit) activation frames must
116   //   be 16 byte-aligned;  see "Mac OS X ABI Function Call Guide"
117   return 16;
118 #endif
119 }
120 
121 // static
AllocatePageSize()122 size_t OS::AllocatePageSize() { return kSbMemoryPageSize; }
123 
124 // static
CommitPageSize()125 size_t OS::CommitPageSize() { return kSbMemoryPageSize; }
126 
127 // static
SetRandomMmapSeed(int64_t seed)128 void OS::SetRandomMmapSeed(int64_t seed) { SB_NOTIMPLEMENTED(); }
129 
130 // static
GetRandomMmapAddr()131 void* OS::GetRandomMmapAddr() { return nullptr; }
132 
Allocate(void * address,size_t size,OS::MemoryPermission access)133 void* Allocate(void* address, size_t size, OS::MemoryPermission access) {
134   SbMemoryMapFlags sb_flags;
135   switch (access) {
136     case OS::MemoryPermission::kNoAccess:
137       sb_flags = SbMemoryMapFlags(0);
138       break;
139     case OS::MemoryPermission::kReadWrite:
140       sb_flags = SbMemoryMapFlags(kSbMemoryMapProtectReadWrite);
141       break;
142     default:
143       SB_LOG(ERROR) << "The requested memory allocation access is not"
144                        " implemented for Starboard: "
145                     << static_cast<int>(access);
146       return nullptr;
147   }
148   void* result = SbMemoryMap(size, sb_flags, "v8::Base::Allocate");
149   if (result == SB_MEMORY_MAP_FAILED) {
150     return nullptr;
151   }
152   return result;
153 }
154 
155 // static
Allocate(void * address,size_t size,size_t alignment,MemoryPermission access)156 void* OS::Allocate(void* address, size_t size, size_t alignment,
157                    MemoryPermission access) {
158   size_t page_size = AllocatePageSize();
159   DCHECK_EQ(0, size % page_size);
160   DCHECK_EQ(0, alignment % page_size);
161   address = AlignedAddress(address, alignment);
162   // Add the maximum misalignment so we are guaranteed an aligned base address.
163   size_t request_size = size + (alignment - page_size);
164   request_size = RoundUp(request_size, OS::AllocatePageSize());
165   void* result = base::Allocate(address, request_size, access);
166   if (result == nullptr) return nullptr;
167 
168   // Unmap memory allocated before the aligned base address.
169   uint8_t* base = static_cast<uint8_t*>(result);
170   uint8_t* aligned_base = reinterpret_cast<uint8_t*>(
171       RoundUp(reinterpret_cast<uintptr_t>(base), alignment));
172   if (aligned_base != base) {
173     DCHECK_LT(base, aligned_base);
174     size_t prefix_size = static_cast<size_t>(aligned_base - base);
175     CHECK(Free(base, prefix_size));
176     request_size -= prefix_size;
177   }
178   // Unmap memory allocated after the potentially unaligned end.
179   if (size != request_size) {
180     DCHECK_LT(size, request_size);
181     size_t suffix_size = request_size - size;
182     CHECK(Free(aligned_base + size, suffix_size));
183     request_size -= suffix_size;
184   }
185 
186   DCHECK_EQ(size, request_size);
187   return static_cast<void*>(aligned_base);
188 }
189 
190 // static
Free(void * address,const size_t size)191 bool OS::Free(void* address, const size_t size) {
192   return SbMemoryUnmap(address, size);
193 }
194 
195 // static
Release(void * address,size_t size)196 bool OS::Release(void* address, size_t size) {
197   return SbMemoryUnmap(address, size);
198 }
199 
200 // static
SetPermissions(void * address,size_t size,MemoryPermission access)201 bool OS::SetPermissions(void* address, size_t size, MemoryPermission access) {
202   SbMemoryMapFlags new_protection;
203   switch (access) {
204     case OS::MemoryPermission::kNoAccess:
205       new_protection = SbMemoryMapFlags(0);
206       break;
207     case OS::MemoryPermission::kRead:
208       new_protection = SbMemoryMapFlags(kSbMemoryMapProtectRead);
209     case OS::MemoryPermission::kReadWrite:
210       new_protection = SbMemoryMapFlags(kSbMemoryMapProtectReadWrite);
211       break;
212     case OS::MemoryPermission::kReadExecute:
213 #if SB_CAN(MAP_EXECUTABLE_MEMORY)
214       new_protection =
215           SbMemoryMapFlags(kSbMemoryMapProtectRead | kSbMemoryMapProtectExec);
216 #else
217       UNREACHABLE();
218 #endif
219       break;
220     default:
221       // All other types are not supported by Starboard.
222       return false;
223   }
224   return SbMemoryProtect(address, size, new_protection);
225 }
226 
227 // static
HasLazyCommits()228 bool OS::HasLazyCommits() {
229   SB_NOTIMPLEMENTED();
230   return false;
231 }
232 
Sleep(TimeDelta interval)233 void OS::Sleep(TimeDelta interval) { SbThreadSleep(interval.InMicroseconds()); }
234 
Abort()235 void OS::Abort() { SbSystemBreakIntoDebugger(); }
236 
DebugBreak()237 void OS::DebugBreak() { SbSystemBreakIntoDebugger(); }
238 
239 class StarboardMemoryMappedFile final : public OS::MemoryMappedFile {
240  public:
241   ~StarboardMemoryMappedFile() final;
memory() const242   void* memory() const final {
243     SB_NOTIMPLEMENTED();
244     return nullptr;
245   }
size() const246   size_t size() const final {
247     SB_NOTIMPLEMENTED();
248     return 0u;
249   }
250 };
251 
252 // static
open(const char * name,FileMode mode)253 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name,
254                                                  FileMode mode) {
255   SB_NOTIMPLEMENTED();
256   return nullptr;
257 }
258 
259 // static
create(const char * name,size_t size,void * initial)260 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name,
261                                                    size_t size, void* initial) {
262   SB_NOTIMPLEMENTED();
263   return nullptr;
264 }
265 
~StarboardMemoryMappedFile()266 StarboardMemoryMappedFile::~StarboardMemoryMappedFile() { SB_NOTIMPLEMENTED(); }
267 
GetCurrentProcessId()268 int OS::GetCurrentProcessId() {
269   SB_NOTIMPLEMENTED();
270   return 0;
271 }
272 
GetCurrentThreadId()273 int OS::GetCurrentThreadId() { return SbThreadGetId(); }
274 
GetLastError()275 int OS::GetLastError() { return SbSystemGetLastError(); }
276 
277 // ----------------------------------------------------------------------------
278 // POSIX stdio support.
279 //
280 
FOpen(const char * path,const char * mode)281 FILE* OS::FOpen(const char* path, const char* mode) {
282   SB_NOTIMPLEMENTED();
283   return nullptr;
284 }
285 
Remove(const char * path)286 bool OS::Remove(const char* path) {
287   SB_NOTIMPLEMENTED();
288   return false;
289 }
290 
DirectorySeparator()291 char OS::DirectorySeparator() { return kSbFileSepChar; }
292 
isDirectorySeparator(const char ch)293 bool OS::isDirectorySeparator(const char ch) {
294   return ch == DirectorySeparator();
295 }
296 
OpenTemporaryFile()297 FILE* OS::OpenTemporaryFile() {
298   SB_NOTIMPLEMENTED();
299   return nullptr;
300 }
301 
302 const char* const OS::LogFileOpenMode = "\0";
303 
Print(const char * format,...)304 void OS::Print(const char* format, ...) {
305   va_list args;
306   va_start(args, format);
307   VPrint(format, args);
308   va_end(args);
309 }
310 
VPrint(const char * format,va_list args)311 void OS::VPrint(const char* format, va_list args) {
312   SbLogRawFormat(format, args);
313 }
314 
FPrint(FILE * out,const char * format,...)315 void OS::FPrint(FILE* out, const char* format, ...) {
316   va_list args;
317   va_start(args, format);
318   VPrintError(format, args);
319   va_end(args);
320 }
321 
VFPrint(FILE * out,const char * format,va_list args)322 void OS::VFPrint(FILE* out, const char* format, va_list args) {
323   SbLogRawFormat(format, args);
324 }
325 
PrintError(const char * format,...)326 void OS::PrintError(const char* format, ...) {
327   va_list args;
328   va_start(args, format);
329   VPrintError(format, args);
330   va_end(args);
331 }
332 
VPrintError(const char * format,va_list args)333 void OS::VPrintError(const char* format, va_list args) {
334   // Starboard has no concept of stderr vs stdout.
335   SbLogRawFormat(format, args);
336 }
337 
SNPrintF(char * str,int length,const char * format,...)338 int OS::SNPrintF(char* str, int length, const char* format, ...) {
339   va_list args;
340   va_start(args, format);
341   int result = VSNPrintF(str, length, format, args);
342   va_end(args);
343   return result;
344 }
345 
VSNPrintF(char * str,int length,const char * format,va_list args)346 int OS::VSNPrintF(char* str, int length, const char* format, va_list args) {
347   int n = SbStringFormat(str, length, format, args);
348   if (n < 0 || n >= length) {
349     // If the length is zero, the assignment fails.
350     if (length > 0) str[length - 1] = '\0';
351     return -1;
352   } else {
353     return n;
354   }
355 }
356 
357 // ----------------------------------------------------------------------------
358 // POSIX string support.
359 //
360 
StrNCpy(char * dest,int length,const char * src,size_t n)361 void OS::StrNCpy(char* dest, int length, const char* src, size_t n) {
362   SbStringCopy(dest, src, n);
363 }
364 
365 // ----------------------------------------------------------------------------
366 // POSIX thread support.
367 //
368 
369 class Thread::PlatformData {
370  public:
PlatformData()371   PlatformData() : thread_(kSbThreadInvalid) {}
372   SbThread thread_;  // Thread handle for pthread.
373   // Synchronizes thread creation
374   Mutex thread_creation_mutex_;
375 };
376 
Thread(const Options & options)377 Thread::Thread(const Options& options)
378     : data_(new PlatformData),
379       stack_size_(options.stack_size()),
380       start_semaphore_(nullptr) {
381   set_name(options.name());
382 }
383 
~Thread()384 Thread::~Thread() { delete data_; }
385 
SetThreadName(const char * name)386 static void SetThreadName(const char* name) { SbThreadSetName(name); }
387 
ThreadEntry(void * arg)388 static void* ThreadEntry(void* arg) {
389   Thread* thread = reinterpret_cast<Thread*>(arg);
390   // We take the lock here to make sure that pthread_create finished first since
391   // we don't know which thread will run first (the original thread or the new
392   // one).
393   { LockGuard<Mutex> lock_guard(&thread->data()->thread_creation_mutex_); }
394   SetThreadName(thread->name());
395   // DCHECK_NE(thread->data()->thread_, kNoThread);
396   thread->NotifyStartedAndRun();
397 
398   return nullptr;
399 }
400 
set_name(const char * name)401 void Thread::set_name(const char* name) {
402   strncpy(name_, name, sizeof(name_));
403   name_[sizeof(name_) - 1] = '\0';
404 }
405 
Start()406 bool Thread::Start() {
407   data_->thread_ =
408       SbThreadCreate(stack_size_, kSbThreadNoPriority, kSbThreadNoAffinity,
409                      true, name_, ThreadEntry, this);
410   return SbThreadIsValid(data_->thread_);
411 }
412 
Join()413 void Thread::Join() { SbThreadJoin(data_->thread_, nullptr); }
414 
CreateThreadLocalKey()415 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
416   return SbThreadCreateLocalKey(nullptr);
417 }
418 
DeleteThreadLocalKey(LocalStorageKey key)419 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
420   SbThreadDestroyLocalKey(key);
421 }
422 
GetThreadLocal(LocalStorageKey key)423 void* Thread::GetThreadLocal(LocalStorageKey key) {
424   return SbThreadGetLocalValue(key);
425 }
426 
SetThreadLocal(LocalStorageKey key,void * value)427 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
428   bool result = SbThreadSetLocalValue(key, value);
429   DCHECK(result);
430 }
431 
432 class StarboardTimezoneCache : public TimezoneCache {
433  public:
Clear(TimeZoneDetection time_zone_detection)434   void Clear(TimeZoneDetection time_zone_detection) override {}
~StarboardTimezoneCache()435   ~StarboardTimezoneCache() override {}
436 
437  protected:
438   static const int msPerSecond = 1000;
439 };
440 
441 class StarboardDefaultTimezoneCache : public StarboardTimezoneCache {
442  public:
LocalTimezone(double time_ms)443   const char* LocalTimezone(double time_ms) override {
444     return SbTimeZoneGetName();
445   }
LocalTimeOffset(double time_ms,bool is_utc)446   double LocalTimeOffset(double time_ms, bool is_utc) override {
447     // SbTimeZOneGetCurrent returns an offset west of Greenwich, which has the
448     // opposite sign V8 expects.
449     // The starboard function returns offset in minutes. We convert to return
450     // value in milliseconds.
451     return SbTimeZoneGetCurrent() * 60.0 * msPerSecond * (-1);
452   }
DaylightSavingsOffset(double time_ms)453   double DaylightSavingsOffset(double time_ms) override {
454     EzTimeValue value = EzTimeValueFromSbTime(SbTimeGetNow());
455     EzTimeExploded ez_exploded;
456     bool result =
457         EzTimeValueExplode(&value, kEzTimeZoneLocal, &ez_exploded, NULL);
458     return ez_exploded.tm_isdst > 0 ? 3600 * msPerSecond : 0;
459   }
460 
~StarboardDefaultTimezoneCache()461   ~StarboardDefaultTimezoneCache() override {}
462 };
463 
CreateTimezoneCache()464 TimezoneCache* OS::CreateTimezoneCache() {
465   return new StarboardDefaultTimezoneCache();
466 }
467 
GetSharedLibraryAddresses()468 std::vector<OS::SharedLibraryAddress> OS::GetSharedLibraryAddresses() {
469   SB_NOTIMPLEMENTED();
470   return {};
471 }
472 
SignalCodeMovingGC()473 void OS::SignalCodeMovingGC() { SB_NOTIMPLEMENTED(); }
474 
AdjustSchedulingParams()475 void OS::AdjustSchedulingParams() {}
476 
DiscardSystemPages(void * address,size_t size)477 bool OS::DiscardSystemPages(void* address, size_t size) {
478   // Starboard API does not support this function yet.
479   return true;
480 }
481 
482 // static
GetCurrentStackPosition()483 Stack::StackSlot Stack::GetCurrentStackPosition() {
484   void* addresses[kStackSize];
485   const size_t count = SbSystemGetStack(addresses, kStackSize);
486   if (count > 0) {
487     return addresses[0];
488   } else {
489     return nullptr;
490   }
491 }
492 
493 }  // namespace base
494 }  // namespace v8
495