1/*
2 * Copyright (c) 2017-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <mach/mach_time.h>
29#include <mach/clock_types.h>
30#include <kern/misc_protos.h>
31#include <kern/clock.h>
32#include <kern/remote_time.h>
33#include <kern/spl.h>
34#include <kern/locks.h>
35#include <sys/kdebug.h>
36#include <machine/machine_routines.h>
37#include <kern/assert.h>
38#include <kern/kern_types.h>
39#include <kern/thread.h>
40#include <machine/commpage.h>
41#include <machine/atomic.h>
42
43LCK_GRP_DECLARE(bt_lck_grp, "bridge timestamp");
44LCK_SPIN_DECLARE(bt_spin_lock, &bt_lck_grp);
45LCK_SPIN_DECLARE(bt_ts_conversion_lock, &bt_lck_grp);
46LCK_SPIN_DECLARE(bt_maintenance_lock, &bt_lck_grp);
47
48#if CONFIG_MACH_BRIDGE_SEND_TIME
49
50uint32_t bt_enable_flag = 0;
51_Atomic uint32_t bt_init_flag = 0;
52
53void mach_bridge_timer_maintenance(void);
54uint32_t mach_bridge_timer_enable(uint32_t new_value, int change);
55
56/*
57 * When CONFIG_MACH_BRIDGE_SEND_TIME is defined, it is expected
58 * that a machine-specific timestamp sending routine such as
59 * void mach_bridge_send_timestamp(uint64_t); has also been defined.
60 */
61extern void mach_bridge_send_timestamp(uint64_t);
62
63void
64mach_bridge_timer_maintenance(void)
65{
66 if (!os_atomic_load(&bt_init_flag, acquire)) {
67 return;
68 }
69
70 lck_spin_lock(&bt_maintenance_lock);
71 if (!bt_enable_flag) {
72 goto done;
73 }
74 mach_bridge_send_timestamp(0);
75
76done:
77 lck_spin_unlock(&bt_maintenance_lock);
78}
79
80/*
81 * If change = 0, return the current value of bridge_timer_enable
82 * If change = 1, update bridge_timer_enable and return the updated
83 * value
84 */
85uint32_t
86mach_bridge_timer_enable(uint32_t new_value, int change)
87{
88 uint32_t current_value = 0;
89 assert(os_atomic_load(&bt_init_flag, relaxed));
90 lck_spin_lock(&bt_maintenance_lock);
91 if (change) {
92 bt_enable_flag = new_value;
93 }
94 current_value = bt_enable_flag;
95 lck_spin_unlock(&bt_maintenance_lock);
96 return current_value;
97}
98
99#endif /* CONFIG_MACH_BRIDGE_SEND_TIME */
100
101#if CONFIG_MACH_BRIDGE_RECV_TIME
102#include <machine/machine_remote_time.h>
103
104/*
105 * functions used by machine-specific code
106 * that implements CONFIG_MACH_BRIDGE_RECV_TIME
107 */
108void mach_bridge_add_timestamp(uint64_t remote_timestamp, uint64_t local_timestamp);
109void bt_calibration_thread_start(void);
110void bt_params_add(struct bt_params *params);
111
112/* function called by sysctl */
113struct bt_params bt_params_get_latest(void);
114
115/*
116 * Platform specific bridge time receiving interface.
117 * These variables should be exported by the platform specific time receiving code.
118 */
119extern _Atomic uint32_t bt_init_flag;
120
121static uint64_t received_local_timestamp = 0;
122static uint64_t received_remote_timestamp = 0;
123/*
124 * Buffer the previous timestamp pairs and rate
125 * It is protected by the bt_ts_conversion_lock
126 */
127#define BT_PARAMS_COUNT 10
128static struct bt_params bt_params_hist[BT_PARAMS_COUNT] = {};
129static int bt_params_idx = -1;
130
131void
132bt_params_add(struct bt_params *params)
133{
134 lck_spin_assert(lck: &bt_ts_conversion_lock, LCK_ASSERT_OWNED);
135
136 bt_params_idx = (bt_params_idx + 1) % BT_PARAMS_COUNT;
137 bt_params_hist[bt_params_idx] = *params;
138}
139
140#if defined(XNU_TARGET_OS_BRIDGE)
141static inline struct bt_params*
142bt_params_find(uint64_t local_ts)
143{
144 lck_spin_assert(&bt_ts_conversion_lock, LCK_ASSERT_OWNED);
145
146 int idx = bt_params_idx;
147 if (idx < 0) {
148 return NULL;
149 }
150 do {
151 if (local_ts >= bt_params_hist[idx].base_local_ts) {
152 return &bt_params_hist[idx];
153 }
154 if (--idx < 0) {
155 idx = BT_PARAMS_COUNT - 1;
156 }
157 } while (idx != bt_params_idx);
158
159 return NULL;
160}
161#endif /* defined(XNU_TARGET_OS_BRIDGE) */
162
163static inline struct bt_params
164bt_params_get_latest_locked(void)
165{
166 lck_spin_assert(lck: &bt_ts_conversion_lock, LCK_ASSERT_OWNED);
167
168 struct bt_params latest_params = {};
169 if (bt_params_idx >= 0) {
170 latest_params = bt_params_hist[bt_params_idx];
171 }
172
173 return latest_params;
174}
175
176struct bt_params
177bt_params_get_latest(void)
178{
179 struct bt_params latest_params = {};
180
181 /* Check if ts_converison_lock has been initialized */
182 if (os_atomic_load(&bt_init_flag, acquire)) {
183 lck_spin_lock(lck: &bt_ts_conversion_lock);
184 latest_params = bt_params_get_latest_locked();
185 lck_spin_unlock(lck: &bt_ts_conversion_lock);
186 }
187 return latest_params;
188}
189
190/*
191 * Conditions: bt_spin_lock held and called from primary interrupt context
192 */
193void
194mach_bridge_add_timestamp(uint64_t remote_timestamp, uint64_t local_timestamp)
195{
196 lck_spin_assert(lck: &bt_spin_lock, LCK_ASSERT_OWNED);
197
198 /* sleep/wake might return the same mach_absolute_time as the previous timestamp pair */
199 if ((received_local_timestamp == local_timestamp) ||
200 (received_remote_timestamp == remote_timestamp)) {
201 return;
202 }
203
204 received_local_timestamp = local_timestamp;
205 received_remote_timestamp = remote_timestamp;
206 thread_wakeup((event_t)bt_params_hist);
207}
208
209static double
210mach_bridge_compute_rate(uint64_t new_local_ts, uint64_t new_remote_ts,
211 uint64_t old_local_ts, uint64_t old_remote_ts)
212{
213 int64_t rdiff = (int64_t)new_remote_ts - (int64_t)old_remote_ts;
214 int64_t ldiff = (int64_t)new_local_ts - (int64_t)old_local_ts;
215 double calc_rate = ((double)rdiff) / (double)ldiff;
216 return calc_rate;
217}
218
219#define MAX_RECALCULATE_COUNT 8
220#define CUMULATIVE_RATE_DECAY_CONSTANT 0.01
221#define CUMULATIVE_RATE_WEIGHT 0.99
222#define INITIAL_RATE 1.0
223#define MIN_INITIAL_SAMPLE_COUNT 10
224#define MAX_INITIAL_SAMPLE_COUNT 50
225#define MAX_SKIP_RESET_COUNT 2
226#define MIN_LOCAL_TS_DISTANCE_NS 100000000 /* 100 ms */
227#define MAX_LOCAL_TS_DISTANCE_NS 350000000 /* 350 ms */
228#define TS_PAIR_MISMATCH_THRESHOLD_NS 50000000 /* 50 ms */
229#define MAX_TS_PAIR_MISMATCHES 5
230#define MAX_TS_PAIR_MISMATCH_RESET_COUNT 3
231#define MIN_OBSERVED_RATE 0.8
232#define MAX_OBSERVED_RATE 1.2
233
234static void
235bt_calibration_thread(void)
236{
237 static uint64_t prev_local_ts = 0, prev_remote_ts = 0, curr_local_ts = 0, curr_remote_ts = 0;
238 static uint64_t prev_received_local_ts = 0, prev_received_remote_ts = 0;
239 static double cumulative_rate = INITIAL_RATE;
240 static uint32_t initial_sample_count = 1;
241 static uint32_t max_initial_sample_count = MAX_INITIAL_SAMPLE_COUNT;
242 static uint32_t skip_reset_count = MAX_SKIP_RESET_COUNT;
243 int recalculate_count = 1;
244 static bool reset = false;
245 bool sleep = false;
246 static bool skip_rcv_ts = false;
247 static uint64_t ts_pair_mismatch = 0;
248 static uint32_t ts_pair_mismatch_reset_count = 0;
249 spl_t s = splsched();
250 lck_spin_lock(lck: &bt_spin_lock);
251 if (!received_remote_timestamp) {
252 if (PE_parse_boot_argn(arg_string: "rt_ini_count", arg_ptr: &max_initial_sample_count,
253 max_arg: sizeof(uint32_t)) == TRUE) {
254 if (max_initial_sample_count < MIN_INITIAL_SAMPLE_COUNT) {
255 max_initial_sample_count = MIN_INITIAL_SAMPLE_COUNT;
256 }
257 }
258 /* Nothing to do the first time */
259 goto block;
260 }
261 /*
262 * The values in bt_params are recalculated every time a new timestamp
263 * pair is received. Firstly, both timestamps are converted to nanoseconds.
264 * The current and previous timestamp pairs are used to compute the
265 * observed_rate of the two clocks w.r.t each other. For the first
266 * MIN_INITIAL_SAMPLE_COUNT number of pairs, the cumulative_rate is a simple
267 * average of the observed_rate. For the later pairs, the cumulative_rate
268 * is updated using exponential moving average of the observed_rate.
269 * The current and bt_params' base timestamp pairs are used to compute
270 * the rate_from_base. This value ensures that the bt_params base
271 * timestamp pair curve doesn't stay parallel to the observed timestamp
272 * pair curve, rather moves in the direction of the observed timestamp curve.
273 * The bt_params.rate is computed as a weighted average of the cumulative_rate
274 * and the rate_from_base. For each current local timestamp, the remote_time
275 * is predicted using the previous values of bt_params. After computing the new
276 * bt_params.rate, bt_params.base_remote_time is set to this predicted value
277 * and bt_params.base_local_time is set to the current local timestamp.
278 */
279recalculate:
280 assertf(recalculate_count <= MAX_RECALCULATE_COUNT, "bt_caliberation_thread: recalculate \
281 invocation exceeds MAX_RECALCULATE_COUNT");
282
283 if ((received_remote_timestamp == BT_RESET_SENTINEL_TS) || (received_remote_timestamp == BT_WAKE_SENTINEL_TS)) {
284 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), received_local_timestamp, received_remote_timestamp, 1);
285 reset = true;
286 skip_reset_count = MAX_SKIP_RESET_COUNT;
287 ts_pair_mismatch_reset_count = 0;
288 goto block;
289 } else if (received_remote_timestamp == BT_SLEEP_SENTINEL_TS) {
290 sleep = true;
291 } else if (!received_local_timestamp) {
292 /* If the local timestamp isn't accurately captured, the received value will be ignored */
293 skip_rcv_ts = true;
294 goto block;
295 }
296
297 /* Keep a copy of the prev timestamps to compute distance */
298 prev_received_local_ts = curr_local_ts;
299 prev_received_remote_ts = curr_remote_ts;
300
301 uint64_t curr_local_abs = received_local_timestamp;
302 absolutetime_to_nanoseconds(abstime: curr_local_abs, result: &curr_local_ts);
303 curr_remote_ts = received_remote_timestamp;
304
305 /* Prevent unusual rate changes caused by delayed timestamps */
306 uint64_t local_diff = curr_local_ts - prev_received_local_ts;
307 if (!(reset || sleep) && ((local_diff < MIN_LOCAL_TS_DISTANCE_NS) ||
308 (!skip_rcv_ts && (local_diff > MAX_LOCAL_TS_DISTANCE_NS)))) {
309 /* Skip the current timestamp */
310 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_SKIP_TS), curr_local_ts, curr_remote_ts,
311 prev_received_local_ts);
312 goto block;
313 } else {
314 skip_rcv_ts = false;
315 /* Use the prev copy of timestamps only if the distance is acceptable */
316 prev_local_ts = prev_received_local_ts;
317 prev_remote_ts = prev_received_remote_ts;
318 }
319 lck_spin_unlock(lck: &bt_spin_lock);
320 splx(s);
321
322 struct bt_params bt_params = {};
323
324 lck_spin_lock(lck: &bt_ts_conversion_lock);
325 if (reset) {
326 if (skip_reset_count > 0) {
327 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_SKIP_TS), curr_local_ts, curr_remote_ts,
328 prev_local_ts, skip_reset_count);
329 skip_reset_count--;
330 goto skip_reset;
331 }
332 bt_params.base_local_ts = curr_local_ts;
333 bt_params.base_remote_ts = curr_remote_ts;
334 bt_params.rate = cumulative_rate;
335 prev_local_ts = 0;
336 prev_remote_ts = 0;
337 ts_pair_mismatch = 0;
338 initial_sample_count = 1;
339 reset = false;
340 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), curr_local_ts, curr_remote_ts, 2);
341 } else if (sleep) {
342 absolutetime_to_nanoseconds(abstime: mach_absolute_time(), result: &bt_params.base_local_ts);
343 bt_params.base_remote_ts = 0;
344 bt_params.rate = 0;
345 sleep = false;
346 } else {
347 struct bt_params bt_params_snapshot = {};
348 if (bt_params_idx >= 0) {
349 bt_params_snapshot = bt_params_hist[bt_params_idx];
350 }
351 lck_spin_unlock(lck: &bt_ts_conversion_lock);
352 if (bt_params_snapshot.rate == 0.0) {
353 /*
354 * The rate should never be 0 because we always expect a reset/wake
355 * sentinel after sleep, followed by valid timestamp pair data that
356 * will be handled by the reset clause (above). However, we should
357 * not rely on a paired version of the remote OS - we could actually
358 * be running a completely different OS! Treat a timestamp after
359 * a sleep as a reset condition.
360 */
361 reset = true;
362 skip_reset_count = MAX_SKIP_RESET_COUNT;
363 ts_pair_mismatch_reset_count = 0;
364 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), curr_local_ts, curr_remote_ts, 3);
365 s = splsched();
366 lck_spin_lock(lck: &bt_spin_lock);
367 goto block;
368 }
369
370 /* Check if the predicted remote timestamp is within the expected current remote timestamp range */
371 uint64_t pred_remote_ts = mach_bridge_compute_timestamp(local_ts_ns: curr_local_ts, params: &bt_params_snapshot);
372 uint64_t diff = 0;
373 if (initial_sample_count >= max_initial_sample_count) {
374 if (pred_remote_ts > curr_remote_ts) {
375 diff = pred_remote_ts - curr_remote_ts;
376 } else {
377 diff = curr_remote_ts - pred_remote_ts;
378 }
379 if (diff > TS_PAIR_MISMATCH_THRESHOLD_NS) {
380 ts_pair_mismatch++;
381 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_TS_MISMATCH), curr_local_ts,
382 curr_remote_ts, pred_remote_ts, ts_pair_mismatch);
383 } else {
384 ts_pair_mismatch = 0;
385 }
386 if (ts_pair_mismatch > MAX_TS_PAIR_MISMATCHES) {
387#if (DEVELOPMENT || DEBUG)
388 if (ts_pair_mismatch_reset_count == MAX_TS_PAIR_MISMATCH_RESET_COUNT) {
389 panic("remote_time: timestamp pair mismatch exceeded limit");
390 }
391#endif /* (DEVELOPMENT || DEBUG) */
392 reset = true;
393 ts_pair_mismatch_reset_count++;
394 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), curr_local_ts, curr_remote_ts, 4);
395 s = splsched();
396 lck_spin_lock(lck: &bt_spin_lock);
397 goto block;
398 }
399 }
400 double observed_rate, rate_from_base, new_rate;
401 observed_rate = mach_bridge_compute_rate(new_local_ts: curr_local_ts, new_remote_ts: curr_remote_ts, old_local_ts: prev_local_ts, old_remote_ts: prev_remote_ts);
402 /* Log bad observed rates and skip the timestamp pair */
403 if ((observed_rate < MIN_OBSERVED_RATE) || (observed_rate > MAX_OBSERVED_RATE)) {
404 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_OBSV_RATE), *(uint64_t *)((void *)&observed_rate));
405 ts_pair_mismatch = ts_pair_mismatch > 0 ? (ts_pair_mismatch - 1) : 0;
406 s = splsched();
407 lck_spin_lock(lck: &bt_spin_lock);
408 goto block;
409 }
410 if (initial_sample_count <= MIN_INITIAL_SAMPLE_COUNT) {
411 initial_sample_count++;
412 cumulative_rate = cumulative_rate + (observed_rate - cumulative_rate) / initial_sample_count;
413 } else {
414 if (initial_sample_count < max_initial_sample_count) {
415 initial_sample_count++;
416 }
417 cumulative_rate = cumulative_rate + CUMULATIVE_RATE_DECAY_CONSTANT * (observed_rate - cumulative_rate);
418 }
419 rate_from_base = mach_bridge_compute_rate(new_local_ts: curr_local_ts, new_remote_ts: curr_remote_ts, old_local_ts: bt_params_snapshot.base_local_ts,
420 old_remote_ts: bt_params_snapshot.base_remote_ts);
421 new_rate = CUMULATIVE_RATE_WEIGHT * cumulative_rate + (1 - CUMULATIVE_RATE_WEIGHT) * rate_from_base;
422 /*
423 * Acquire the lock first to ensure that bt_params.base_local_ts is always
424 * greater than the last value of now captured by mach_bridge_remote_time.
425 * This ensures that we always use the same parameters to compute remote
426 * timestamp for a given local timestamp.
427 */
428 lck_spin_lock(lck: &bt_ts_conversion_lock);
429 absolutetime_to_nanoseconds(abstime: mach_absolute_time(), result: &bt_params.base_local_ts);
430 bt_params.base_remote_ts = mach_bridge_compute_timestamp(local_ts_ns: bt_params.base_local_ts, params: &bt_params_snapshot);
431 bt_params.rate = new_rate;
432 }
433 bt_params_add(params: &bt_params);
434 commpage_set_remotetime_params(rate: bt_params.rate, base_local_ts: bt_params.base_local_ts, base_remote_ts: bt_params.base_remote_ts);
435 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_TS_PARAMS), bt_params.base_local_ts,
436 bt_params.base_remote_ts, *(uint64_t *)((void *)&bt_params.rate));
437
438skip_reset:
439 lck_spin_unlock(lck: &bt_ts_conversion_lock);
440
441 s = splsched();
442 lck_spin_lock(lck: &bt_spin_lock);
443 /* Check if a new timestamp pair was received */
444 if (received_local_timestamp != curr_local_abs) {
445 recalculate_count++;
446 goto recalculate;
447 }
448block:
449 assert_wait(event: (event_t)bt_params_hist, THREAD_UNINT);
450 lck_spin_unlock(lck: &bt_spin_lock);
451 splx(s);
452 thread_block(continuation: (thread_continue_t)bt_calibration_thread);
453}
454
455void
456bt_calibration_thread_start(void)
457{
458 thread_t thread;
459 kern_return_t result = kernel_thread_start_priority(continuation: (thread_continue_t)bt_calibration_thread,
460 NULL, BASEPRI_KERNEL, new_thread: &thread);
461 if (result != KERN_SUCCESS) {
462 panic("mach_bridge_add_timestamp: thread_timestamp_calibration");
463 }
464 thread_deallocate(thread);
465}
466
467#endif /* CONFIG_MACH_BRIDGE_RECV_TIME */
468
469/**
470 * mach_bridge_remote_time
471 *
472 * This function is used to predict the remote CPU's clock time, given
473 * the local time.
474 *
475 * If local_timestamp = 0, then the remote_timestamp is calculated
476 * corresponding to the current mach_absolute_time.
477 *
478 * If XNU_TARGET_OS_BRIDGE is defined, then monotonicity of
479 * predicted time is guaranteed only for recent local_timestamp values
480 * lesser than the current mach_absolute_time upto 1 second.
481 *
482 * If CONFIG_MACH_BRIDGE_SEND_TIME is true, then the function is compiled
483 * for the remote CPU. If CONFIG_MACH_BRIDGE_RECV_TIME is true, then the
484 * the function is compiled for the local CPU. Both config options cannot
485 * be true simultaneously.
486 */
487uint64_t
488mach_bridge_remote_time(uint64_t local_timestamp)
489{
490#if defined(CONFIG_MACH_BRIDGE_SEND_TIME)
491#if !defined(CONFIG_MACH_BRIDGE_RECV_TIME)
492 /* only send side of the bridge is defined: no translation needed */
493 if (!local_timestamp) {
494 return mach_absolute_time();
495 }
496 return 0;
497#else
498#error "You cannot define both sides of the bridge!"
499#endif /* !defined(CONFIG_MACH_BRIDGE_RECV_TIME) */
500#else
501#if !defined(CONFIG_MACH_BRIDGE_RECV_TIME)
502 /* neither the send or receive side of the bridge is defined: echo the input */
503 return local_timestamp;
504#else
505 if (!os_atomic_load(&bt_init_flag, acquire)) {
506 return 0;
507 }
508
509 uint64_t remote_timestamp = 0;
510
511 lck_spin_lock(lck: &bt_ts_conversion_lock);
512 uint64_t now = mach_absolute_time();
513 if (!local_timestamp) {
514 local_timestamp = now;
515 }
516#if defined(XNU_TARGET_OS_BRIDGE)
517 uint64_t local_timestamp_ns = 0;
518 if (local_timestamp < now) {
519 absolutetime_to_nanoseconds(local_timestamp, &local_timestamp_ns);
520 struct bt_params *params = bt_params_find(local_timestamp_ns);
521 remote_timestamp = mach_bridge_compute_timestamp(local_timestamp_ns, params);
522 }
523#else
524 struct bt_params params = bt_params_get_latest_locked();
525 remote_timestamp = mach_bridge_compute_timestamp(local_ts_ns: local_timestamp, params: &params);
526#endif /* defined(XNU_TARGET_OS_BRIDGE) */
527 lck_spin_unlock(lck: &bt_ts_conversion_lock);
528 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_REMOTE_TIME), local_timestamp, remote_timestamp, now);
529
530 return remote_timestamp;
531#endif /* !defined(CONFIG_MACH_BRIDGE_RECV_TIME) */
532#endif /* defined(CONFIG_MACH_BRIDGE_SEND_TIME) */
533}
534