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