first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2011 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <arch.h>
|
||||
#include <lib/heap.h>
|
||||
#include <lib/profile.h>
|
||||
#include <lib/libc.h>
|
||||
#include <platform.h>
|
||||
#include <sys.h>
|
||||
#include <sys/boot.h>
|
||||
|
||||
typedef void (* entry_func_t)(void *, int, int, int);
|
||||
typedef void (* trampoline_func_t)(entry_func_t, void *);
|
||||
|
||||
/* quiesce hardware and jump into a new image */
|
||||
void prepare_and_jump(enum boot_target boot, void *ptr, void *arg)
|
||||
{
|
||||
entry_func_t entry = (entry_func_t)(uintptr_t)ptr;
|
||||
trampoline_func_t trampoline = (trampoline_func_t)platform_get_boot_trampoline();
|
||||
|
||||
/* keep alive a little longer */
|
||||
platform_watchdog_tickle();
|
||||
|
||||
/* do whatever prep needs to happen before jumping into something */
|
||||
platform_bootprep(boot);
|
||||
|
||||
/* make sure timers and whatnot aren't running */
|
||||
platform_quiesce_hardware(boot);
|
||||
|
||||
/* verify we haven't had any heap corruption */
|
||||
heap_verify();
|
||||
|
||||
/* make sure interrupts are disabled */
|
||||
enter_critical_section();
|
||||
|
||||
/* Copy profile buffer from SRAM into panic RAM */
|
||||
PROFILE_HANDOFF();
|
||||
|
||||
/* quiesce the cpu, disabling the mmu and caches */
|
||||
arch_cpu_quiesce();
|
||||
|
||||
if (trampoline != NULL)
|
||||
trampoline(entry, arg);
|
||||
else
|
||||
entry(arg, 0, 0, 0);
|
||||
|
||||
/* should not get here */
|
||||
for(;;) ;
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <list.h>
|
||||
#include <sys.h>
|
||||
#include <sys/callout.h>
|
||||
#include <platform/timer.h>
|
||||
|
||||
static struct list_node callout_queue;
|
||||
|
||||
static int callout_init(void);
|
||||
static int _callout_dequeue(struct callout *c);
|
||||
static void _callout_reset_deadline(void);
|
||||
static void callout_deadline(void);
|
||||
|
||||
static bool ready;
|
||||
|
||||
/*
|
||||
* Initialise the callout system.
|
||||
*/
|
||||
static int callout_init(void)
|
||||
{
|
||||
list_initialize(&callout_queue);
|
||||
|
||||
ready = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue (func) to be called after at least (delay) microseconds.
|
||||
*/
|
||||
void callout_enqueue(struct callout *c, utime_t delay, callout_func func, void *arg)
|
||||
{
|
||||
struct callout *entry;
|
||||
uint64_t now;
|
||||
|
||||
/* if we have not been initialised, do so now */
|
||||
if (unlikely(!ready))
|
||||
callout_init();
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
/* remove it if it's already in a list */
|
||||
_callout_dequeue(c);
|
||||
|
||||
/* convert utime_t delay to absolute clock deadline */
|
||||
now = timer_get_ticks();
|
||||
c->sched_ticks = now + timer_usecs_to_ticks(delay);
|
||||
// printf("callout %p: %lluus at %llu ticks (now %llu)\n", c, delay, c->sched_ticks, now);
|
||||
c->delay = delay;
|
||||
c->callback = func;
|
||||
c->arg = arg;
|
||||
|
||||
list_for_every_entry(&callout_queue, entry, struct callout, list) {
|
||||
/* search through the callout queue, finding the right sorted slot */
|
||||
if (c->sched_ticks < entry->sched_ticks) {
|
||||
list_add_before(&entry->list, &c->list);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* either the queue was empty or our entry is the last */
|
||||
list_add_tail(&callout_queue, &c->list);
|
||||
|
||||
done:
|
||||
_callout_reset_deadline();
|
||||
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue the callout (c)
|
||||
*/
|
||||
int callout_dequeue(struct callout *c)
|
||||
{
|
||||
int err;
|
||||
|
||||
enter_critical_section();
|
||||
err = _callout_dequeue(c);
|
||||
exit_critical_section();
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the callout (c) to be called after (newdelay) microseconds.
|
||||
*/
|
||||
int callout_reset(struct callout *c, utime_t newdelay)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
/* remove it if it's already in a list */
|
||||
_callout_dequeue(c);
|
||||
|
||||
if (newdelay != 0)
|
||||
c->delay = newdelay;
|
||||
|
||||
if (c->delay == 0) {
|
||||
err = -1;
|
||||
} else {
|
||||
callout_enqueue(c, c->delay, c->callback, c->arg);
|
||||
}
|
||||
|
||||
exit_critical_section();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the timer deadline as the queue may have changed.
|
||||
*/
|
||||
static void _callout_reset_deadline(void)
|
||||
{
|
||||
struct callout *c;
|
||||
|
||||
c = list_peek_head_type(&callout_queue, struct callout, list);
|
||||
|
||||
if (c == NULL) {
|
||||
timer_deadline_enter(0, NULL);
|
||||
} else {
|
||||
timer_deadline_enter(c->sched_ticks, callout_deadline);
|
||||
}
|
||||
}
|
||||
|
||||
void callout_reset_deadline(void)
|
||||
{
|
||||
enter_critical_section();
|
||||
_callout_reset_deadline();
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull callout (c) from the queue (while locked)
|
||||
*/
|
||||
static int _callout_dequeue(struct callout *c)
|
||||
{
|
||||
|
||||
if (list_in_list(&c->list)) {
|
||||
list_delete(&c->list);
|
||||
_callout_reset_deadline();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback from the timer when the deadline expires; call all of the
|
||||
* pending callouts and reschedule the deadline accordingly.
|
||||
*
|
||||
* Note that the timer is allowed to call us early; if we have set a
|
||||
* deadline beyond its ability to handle we will do no work and just re-schedule.
|
||||
*/
|
||||
static void callout_deadline(void)
|
||||
{
|
||||
struct callout *c;
|
||||
uint64_t t;
|
||||
|
||||
while ((c = list_peek_head_type(&callout_queue, struct callout, list)) != NULL) {
|
||||
t = timer_get_ticks();
|
||||
|
||||
/* if the event has expired */
|
||||
if (c->sched_ticks <= t) {
|
||||
// printf("callout %p: invoked, scheduled %llu now %llu\n", c, c->sched_ticks, t);
|
||||
|
||||
/* remove from the list and call out */
|
||||
list_delete(&c->list);
|
||||
c->callback(c, c->arg);
|
||||
} else {
|
||||
|
||||
/* callout in the future, schedule callback and we're done */
|
||||
timer_deadline_enter(c->sched_ticks, callout_deadline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2008 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <arch.h>
|
||||
#include <sys.h>
|
||||
#include <sys/task.h>
|
||||
#include <debug.h>
|
||||
#include <lib/cbuf.h>
|
||||
#include <drivers/power.h>
|
||||
|
||||
#if WITH_HW_UART
|
||||
# include <drivers/uart.h>
|
||||
# if WITH_TARGET_CONFIG
|
||||
# include <target/uartconfig.h>
|
||||
# elif WITH_PLATFORM_UARTCONFIG
|
||||
# include <platform/uartconfig.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if APPLICATION_SECUREROM
|
||||
# define DEBUG_QUEUE_LEN 256
|
||||
#else
|
||||
# define DEBUG_QUEUE_LEN 4096
|
||||
#endif
|
||||
static struct task_event debug_event;
|
||||
static struct cbuf *debug_cbuf;
|
||||
|
||||
#if WITH_USB_MODE_RECOVERY
|
||||
|
||||
// Use USB serial debug, unless there's a dockfifo, because spew from that
|
||||
// driver will deadlock us.
|
||||
#if !defined(USE_USB_DEBUG) && !defined(WITH_HW_DOCKFIFO_BULK)
|
||||
#define USE_USB_DEBUG 1
|
||||
#endif
|
||||
|
||||
#include <drivers/usb/usb_public.h>
|
||||
|
||||
#endif // WITH_USB_MODE_RECOVERY
|
||||
|
||||
#if WITH_TBT_MODE_RECOVERY
|
||||
|
||||
#ifndef USE_TBT_DEBUG
|
||||
#define USE_TBT_DEBUG 1
|
||||
#endif
|
||||
|
||||
#include <drivers/thunderbolt/thunderboot.h>
|
||||
|
||||
#endif // WITH_TBT_MODE_RECOVERY
|
||||
|
||||
#if WITH_ARM_DCC
|
||||
# include <arch/arm/arm.h>
|
||||
#endif
|
||||
|
||||
#if WITH_SHM_CONSOLE
|
||||
# include <drivers/shmcon.h>
|
||||
#endif
|
||||
|
||||
#if WITH_HW_DOCKFIFO_UART
|
||||
# include <drivers/dockfifo/dockfifo.h>
|
||||
#endif
|
||||
|
||||
#if WITH_HW_DOCKCHANNEL_UART
|
||||
# include <drivers/dockchannel/dockchannel.h>
|
||||
#endif
|
||||
|
||||
int debug_getchar(void)
|
||||
{
|
||||
ssize_t ret;
|
||||
char c;
|
||||
|
||||
ASSERT(NULL != debug_cbuf);
|
||||
|
||||
event_wait(&debug_event);
|
||||
ret = cbuf_read_char(debug_cbuf, &c);
|
||||
ASSERT(ret >= 0);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int debug_getchar_nowait(void)
|
||||
{
|
||||
ssize_t ret;
|
||||
char c;
|
||||
|
||||
ASSERT(NULL != debug_cbuf);
|
||||
|
||||
ret = cbuf_read_char(debug_cbuf, &c);
|
||||
if (ret < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int debug_pushchar(int c)
|
||||
{
|
||||
ASSERT(NULL != debug_cbuf);
|
||||
return cbuf_write_char(debug_cbuf, c);
|
||||
}
|
||||
|
||||
void debug_init(void)
|
||||
{
|
||||
debug_cbuf = cbuf_create(DEBUG_QUEUE_LEN, &debug_event);
|
||||
}
|
||||
|
||||
|
||||
int DebugUartReady = 0;
|
||||
|
||||
void debug_enable_uarts(int debug_uarts)
|
||||
{
|
||||
DebugUartReady |= debug_uarts;
|
||||
}
|
||||
|
||||
void debug_putchar(int c)
|
||||
{
|
||||
#if USE_USB_DEBUG
|
||||
usb_serial_putchar(c);
|
||||
#endif
|
||||
|
||||
#if USE_TBT_DEBUG
|
||||
thunderboot_putchar(c);
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_SERIAL_PORT)
|
||||
if ((DebugUartReady & kPowerNVRAMiBootDebugIAPSerial) != 0) {
|
||||
if (c == '\n')
|
||||
uart_putc(DEBUG_SERIAL_PORT, '\r');
|
||||
uart_putc(DEBUG_SERIAL_PORT, c);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_SERIAL_PORT2)
|
||||
if ((DebugUartReady & kPowerNVRAMiBootDebugAltSerial) != 0) {
|
||||
if (c == '\n')
|
||||
uart_putc(DEBUG_SERIAL_PORT2, '\r');
|
||||
uart_putc(DEBUG_SERIAL_PORT2, c);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_ARM_DCC
|
||||
// always output dcc
|
||||
arm_write_dcc_char(c);
|
||||
#endif
|
||||
|
||||
#if WITH_SHM_CONSOLE
|
||||
if ((DebugUartReady & kPowerNVRAMiBootDebugJtag) != 0) {
|
||||
if (c == '\n')
|
||||
shmcon_putc(0, '\r');
|
||||
shmcon_putc(0, c);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_HW_DOCKFIFO_UART
|
||||
if ((DebugUartReady & kPowerNVRAMiBootDebugIAPSerial) != 0) {
|
||||
if (c == '\n')
|
||||
dockfifo_uart_putc('\r');
|
||||
dockfifo_uart_putc(c);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_HW_DOCKCHANNEL_UART
|
||||
if ((DebugUartReady & kPowerNVRAMiBootDebugIAPSerial) != 0) {
|
||||
if (c == '\n')
|
||||
dockchannel_uart_putc('\r');
|
||||
dockchannel_uart_putc(c);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WITH_APPLICATION_PUTCHAR
|
||||
application_putchar(c);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int debug_run_script(const char *addr)
|
||||
{
|
||||
char lastchar;
|
||||
|
||||
// printf("running script at %p\n", addr);
|
||||
|
||||
/* start stuffing data into the input queue at the specified address until we hit an EOF char */
|
||||
lastchar = 0;
|
||||
while ((*addr != 0x04) && (*addr != 0)) {
|
||||
debug_pushchar(*addr);
|
||||
lastchar = *addr;
|
||||
addr++;
|
||||
}
|
||||
if (lastchar != '\n')
|
||||
debug_pushchar('\n');
|
||||
|
||||
// printf("finished running script (EOF at %p)\n", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <arch.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/random.h>
|
||||
#include <platform.h>
|
||||
#include <sys.h>
|
||||
#include <sys/boot.h>
|
||||
#include <debug.h>
|
||||
|
||||
#if WITH_CONSISTENT_DBG
|
||||
#include <drivers/consistent_debug.h>
|
||||
#endif
|
||||
|
||||
#if WITH_HW_TIMER
|
||||
#include <platform/timer.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make the panic info easy to find.
|
||||
*/
|
||||
char * gPanicStr; /* pointer to current panic string or NULL if not doing panic */
|
||||
const char * gPanicFunc; /* pointer to function calling panic */
|
||||
#if defined(__LP64__)
|
||||
static char panicBuf[1024]; /* buffer for constructed panic message */
|
||||
#else
|
||||
static char panicBuf[512]; /* buffer for constructed panic message */
|
||||
#endif
|
||||
static unsigned panicDepth = 0;
|
||||
static u_int64_t panicStamp;
|
||||
|
||||
static const char doublePanic[] = "double panic in ";
|
||||
|
||||
void _panic(const char *func, const char *str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
#if WITH_PANIC_HOOKS
|
||||
void ** cursor;
|
||||
struct panic_hook * hook;
|
||||
#endif
|
||||
|
||||
/* There are cases where panic can spiral out of control if the task structure
|
||||
* was demolished. Detect recursion here before doing anything else, and STOP
|
||||
* (with interrupts known to have been masked). */
|
||||
if (++panicDepth > 2)
|
||||
arch_spin();
|
||||
|
||||
/* make sure that interrupts are masked and don't get enabled during panic */
|
||||
enter_critical_section();
|
||||
|
||||
if (NULL != gPanicStr) {
|
||||
/*
|
||||
* We are already trying to panic, so this is very bad.
|
||||
* Do our very best to get the 'double panic' string into
|
||||
* the panic buffer.
|
||||
*/
|
||||
memcpy(panicBuf, doublePanic, sizeof(doublePanic));
|
||||
gPanicFunc = func;
|
||||
strlcat(panicBuf, func, sizeof(panicBuf));
|
||||
|
||||
/* clean the cache so that the panic string is in memory */
|
||||
platform_cache_operation(CACHE_PANIC | CACHE_CLEAN, 0, 0);
|
||||
|
||||
/* and try to print it */
|
||||
puts("\n\n");
|
||||
puts(panicBuf);
|
||||
puts("\n\n");
|
||||
|
||||
/* spin here - not safe to do anything else */
|
||||
arch_spin();
|
||||
}
|
||||
|
||||
#if WITH_HW_TIMER
|
||||
panicStamp = timer_get_ticks();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* First pass through the panic path
|
||||
*/
|
||||
|
||||
/* gPanicFunc can be compared with NULL to test whether we are panicking */
|
||||
gPanicFunc = func;
|
||||
|
||||
/* construct the panic string */
|
||||
gPanicStr = panicBuf;
|
||||
va_start(ap, str);
|
||||
vsnprintf(panicBuf, sizeof(panicBuf), str, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* clean the cache so that the panic string is in memory */
|
||||
platform_cache_operation(CACHE_PANIC | CACHE_CLEAN, 0, 0);
|
||||
|
||||
/* emit it */
|
||||
puts("\npanic: ");
|
||||
puts(func);
|
||||
puts(": ");
|
||||
puts(panicBuf);
|
||||
puts("\n\n");
|
||||
|
||||
#if WITH_CONSISTENT_DBG && (PRODUCT_IBEC || PRODUCT_IBOOT)
|
||||
consistent_debug_update_ap_cpr(DBG_CPR_STATE_CRASHED, DBG_CPR_AP_PANICKED_IN_IBOOT);
|
||||
// Register location of panic string
|
||||
dbg_record_header_t dbghdr;
|
||||
dbghdr.physaddr = (uintptr_t)(mem_static_map_physical((uintptr_t)panicBuf));
|
||||
dbghdr.length = sizeof(panicBuf);
|
||||
dbghdr.record_id = kDbgIdPanicHeaderAP;
|
||||
consistent_debug_register_header(dbghdr);
|
||||
#endif
|
||||
|
||||
#if WITH_PANIC_HOOKS
|
||||
/* run the set of panic handlers */
|
||||
LINKER_SET_FOREACH(cursor, panic_hooks) {
|
||||
hook = (struct panic_hook *)*cursor;
|
||||
hook->func(hook->arg);
|
||||
}
|
||||
|
||||
#endif
|
||||
/* XXX try to drain the console here */
|
||||
|
||||
/* clean the cache again to get console output out in case the application is saving it */
|
||||
platform_cache_operation(CACHE_PANIC | CACHE_CLEAN, 0, 0);
|
||||
|
||||
|
||||
#if !DEBUG_BUILD
|
||||
platform_reset(true);
|
||||
#else
|
||||
halt();
|
||||
#endif
|
||||
}
|
||||
|
||||
void abort(void)
|
||||
{
|
||||
panic("abort");
|
||||
}
|
||||
|
||||
void halt()
|
||||
{
|
||||
printf("\nsystem halted, spinning forever...\n");
|
||||
|
||||
/* try to quiesce hardware to make JTAG easier */
|
||||
//prepare_and_jump(BOOT_HALT, (void *)(uintptr_t)&arch_spin, NULL);
|
||||
arch_spin();
|
||||
}
|
||||
|
||||
#if defined(__SSP_ALL__)
|
||||
#error "Can't compile stack.c with -fstack-protector-all. That'd emit a stack check fault on sys_init_stack!"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Stack overflow testing.
|
||||
*/
|
||||
#ifdef __arm64__
|
||||
#define DEFAULT_STACK_COOKIE 0x4752400444303631ull // 'GRD\0D061'
|
||||
uint64_t __stack_chk_guard __used = DEFAULT_STACK_COOKIE;
|
||||
#else
|
||||
#define DEFAULT_STACK_COOKIE 'GRD\0'
|
||||
uint32_t __stack_chk_guard __used = DEFAULT_STACK_COOKIE;
|
||||
#endif
|
||||
|
||||
#if WITH_RANDOM
|
||||
// this needs to be outside of sys_init_stack_cookie to stop -fstack-protector-strong from tripping
|
||||
static uint8_t stack_chk_guard_zero_byte = 0;
|
||||
#endif
|
||||
|
||||
// WARNING: This function cannot be called by anything that returns "normally"
|
||||
// without risking a false stack overflow panic.
|
||||
void sys_init_stack_cookie(void)
|
||||
{
|
||||
#if WITH_RANDOM
|
||||
uint8_t *guard = (uint8_t*)&__stack_chk_guard;
|
||||
|
||||
// This is tricky. You can't do anything in this function that would
|
||||
// cause the compiler to insert its stack validation cookie into
|
||||
// the stack frame because we're changing the value of the cookie
|
||||
// that the stack check code checks.
|
||||
|
||||
if (random_get_bytes((u_int8_t *)&__stack_chk_guard, sizeof(__stack_chk_guard)) != 0) {
|
||||
// In case of failure, put back the static cookie just in case.
|
||||
__stack_chk_guard = DEFAULT_STACK_COOKIE;
|
||||
}
|
||||
|
||||
// Grab a byte of entropy and use that value as an index to stuff a
|
||||
// zero byte into the cookie to block string functions from reading
|
||||
// past the cookie.
|
||||
random_get_bytes(&stack_chk_guard_zero_byte, sizeof(stack_chk_guard_zero_byte));
|
||||
|
||||
guard[stack_chk_guard_zero_byte & (sizeof(__stack_chk_guard) - 1)] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void __noreturn __used
|
||||
__stack_chk_fail(void)
|
||||
{
|
||||
panic("stack corrupted");
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <sys/hash.h>
|
||||
#include <sys/menu.h>
|
||||
|
||||
#if WITH_SHA2_384
|
||||
void sha384_calculate(const void *buffer, size_t length, void *result, size_t out_len)
|
||||
{
|
||||
RELEASE_ASSERT(out_len >= CCSHA384_OUTPUT_SIZE);
|
||||
ccdigest(ccsha384_di(), length, buffer, result);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !HOST_TEST
|
||||
|
||||
void hash_calculate(const void *in_ptr, size_t in_len, void *out_ptr, size_t out_len)
|
||||
{
|
||||
RELEASE_ASSERT(out_len >= HASH_OUTPUT_SIZE);
|
||||
#if WITH_SHA2_384
|
||||
sha384_calculate(in_ptr, in_len, out_ptr, out_len);
|
||||
#else // SHA1
|
||||
sha1_calculate(in_ptr, in_len, out_ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if WITH_SHA2_384 && WITH_MENU
|
||||
|
||||
int do_sha384(int argc, struct cmd_arg *args)
|
||||
{
|
||||
u_int8_t hash[CCSHA384_OUTPUT_SIZE];
|
||||
u_int32_t cnt;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("not enough arguments.\n");
|
||||
printf("%s <address> <len>\n", args[0].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!security_allow_memory((void *)args[1].u, args[2].u)) {
|
||||
printf("Permission Denied\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sha384_calculate((void *)args[1].u, args[2].u, hash, sizeof(hash));
|
||||
printf("sha384 of 0x%08lx for 0x%08lx bytes: ", args[1].u, args[2].u);
|
||||
for (cnt = 0; cnt < CCSHA384_OUTPUT_SIZE; cnt++)
|
||||
printf("%02x%s", hash[cnt], (cnt != CCSHA384_OUTPUT_SIZE-1) ? ":" : "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MENU_COMMAND_DEVELOPMENT(sha384, do_sha384, "SHA_384 hash of specified memory address range.", NULL);
|
||||
|
||||
#endif // WITH_SHA2_384 && WITH_MENU
|
||||
#endif // !HOST_TEST */
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2011 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <arch.h>
|
||||
#include <compiler.h>
|
||||
#include <lib/env.h>
|
||||
#include <lib/heap.h>
|
||||
#include <lib/mib.h>
|
||||
#include <lib/random.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <sys.h>
|
||||
#include <sys/task.h>
|
||||
#include <sys/security.h>
|
||||
#include <sys/callout.h>
|
||||
|
||||
/* these need to be non-static and initialised for applications that patch them */
|
||||
#define HEAP_SIZE_UNPATCHED ((size_t)-1)
|
||||
void *heap_base = (void *)-1;
|
||||
size_t heap_size = HEAP_SIZE_UNPATCHED;
|
||||
|
||||
MIB_CONSTANT_PTR(kMIBBuildBanner, kOIDTypeString|kOIDDataIndirect, (void *)&build_banner_string);
|
||||
MIB_CONSTANT_PTR(kMIBBuildStyle, kOIDTypeString|kOIDDataIndirect, (void *)&build_style_string);
|
||||
MIB_CONSTANT_PTR(kMIBBuildTag, kOIDTypeString|kOIDDataIndirect, (void *)&build_tag_string);
|
||||
|
||||
/* called from each binary's main() routine to initialize basic system services */
|
||||
void sys_init(void)
|
||||
{
|
||||
/*
|
||||
* Initialise the heap.
|
||||
*
|
||||
* If HEAP_END is defined, we assume that we are going to
|
||||
* configure the heap size using it.
|
||||
*
|
||||
* If HEAP_BASE is defined, we respect the definition. Otherwise
|
||||
* we start the heap at the end of the __DATA segment.
|
||||
*
|
||||
* If HEAP_END is not defined, someone may have patched heap_base/
|
||||
* heap_end, so we check and create a heap with their values if they
|
||||
* have.
|
||||
*/
|
||||
#ifdef HEAP_END
|
||||
# ifdef HEAP_BASE
|
||||
/* we have a defined base for the heap */
|
||||
heap_base = (void *)HEAP_BASE;
|
||||
# else
|
||||
/* create the heap at the end of the __DATA segment */
|
||||
heap_base = __segment_end(__DATA);
|
||||
# endif
|
||||
/* compute the heap size*/
|
||||
heap_size = HEAP_END - (uintptr_t)heap_base;
|
||||
#endif
|
||||
/* if we have computed (or had patched in) a heap size, create the heap */
|
||||
if (heap_size != HEAP_SIZE_UNPATCHED) {
|
||||
#if WITH_RANDOM
|
||||
bool random_heap_cookie = mib_get_bool(kMIBTargetRandomHeapCookie);
|
||||
|
||||
if (random_heap_cookie) {
|
||||
uint8_t heap_cookie[HEAP_COOKIE_SIZE];
|
||||
|
||||
random_get_bytes_noheap(heap_cookie, sizeof(heap_cookie), heap_base, heap_size);
|
||||
heap_set_cookie(heap_cookie);
|
||||
}
|
||||
#endif
|
||||
|
||||
heap_add_chunk(heap_base, heap_size, true);
|
||||
}
|
||||
|
||||
/* initialize security */
|
||||
#if !WITH_NO_SECURITY
|
||||
security_init(false);
|
||||
#endif
|
||||
|
||||
#if WITH_ENV
|
||||
/* populate the environment with some default values */
|
||||
sys_setup_default_environment();
|
||||
#endif
|
||||
|
||||
/* initialize the tasking system */
|
||||
task_init();
|
||||
|
||||
/* after tasks are initialized, do some more cpu init (if needed) */
|
||||
arch_cpu_init_posttasks();
|
||||
|
||||
/* initialize the debug i/o queue */
|
||||
debug_init();
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2006,2009 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <arch.h>
|
||||
#include <stdio.h>
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <sys/task.h>
|
||||
|
||||
#if DEBUG_CRITICAL_SECTIONS
|
||||
void _enter_critical_section(const char *from)
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "enter_critical_section: %s %p %d\n", from, current_task, current_task->irq_disable_count);
|
||||
#else
|
||||
void enter_critical_section(void)
|
||||
{
|
||||
#endif
|
||||
RELEASE_ASSERT(current_task->irq_disable_count >= 0);
|
||||
RELEASE_ASSERT(current_task->irq_disable_count < 1000);
|
||||
current_task->irq_disable_count++;
|
||||
if (current_task->irq_disable_count == 1) {
|
||||
arch_disable_ints();
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_CRITICAL_SECTIONS
|
||||
void _exit_critical_section(const char *from)
|
||||
{
|
||||
dprintf(DEBUG_SPEW, "exit_critical_section: %s %p %d\n", from, current_task, current_task->irq_disable_count);
|
||||
#else
|
||||
void exit_critical_section(void)
|
||||
{
|
||||
#endif
|
||||
RELEASE_ASSERT(current_task->irq_disable_count > 0);
|
||||
current_task->irq_disable_count--;
|
||||
if (current_task->irq_disable_count == 0) {
|
||||
arch_enable_ints();
|
||||
}
|
||||
}
|
||||
|
||||
/* the following are only used in interrupt handler glue */
|
||||
void _irq_enter_critical_section(void)
|
||||
{
|
||||
RELEASE_ASSERT(current_task->irq_disable_count >= 0);
|
||||
RELEASE_ASSERT(current_task->irq_disable_count < 1000);
|
||||
current_task->irq_disable_count += 100;
|
||||
}
|
||||
|
||||
void _irq_exit_critical_section(void)
|
||||
{
|
||||
RELEASE_ASSERT(current_task->irq_disable_count >= 100);
|
||||
current_task->irq_disable_count -= 100;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
/*
|
||||
* Generic memory mapping functions.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys.h>
|
||||
|
||||
static struct mem_static_map_entry *
|
||||
mem_static_find_virtual(uintptr_t addr, ptrdiff_t *offset)
|
||||
{
|
||||
struct mem_static_map_entry *mp;
|
||||
|
||||
for (mp = mem_static_map_entries; mp->size != 0; mp++) {
|
||||
/* is this an entry in a cached mapping? */
|
||||
if ((MAP_NO_ENTRY != mp->cached_base) &&
|
||||
(addr >= mp->cached_base) &&
|
||||
(addr <= (mp->cached_base + mp->size - 1))) {
|
||||
*offset = addr - mp->cached_base;
|
||||
return(mp);
|
||||
}
|
||||
|
||||
/* is this an entry in an uncached mapping? */
|
||||
if ((mp->uncached_base != MAP_NO_ENTRY) &&
|
||||
(addr >= mp->uncached_base) &&
|
||||
(addr <= (mp->uncached_base + mp->size - 1))) {
|
||||
*offset = addr - mp->uncached_base;
|
||||
return(mp);
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
static struct mem_static_map_entry *
|
||||
mem_static_find_physical(uintptr_t addr)
|
||||
{
|
||||
struct mem_static_map_entry *mp;
|
||||
|
||||
for (mp = mem_static_map_entries; mp->size != 0; mp++) {
|
||||
/* is this an entry in a physical mapping? */
|
||||
if ((MAP_NO_ENTRY != mp->physical_base) &&
|
||||
(addr >= mp->physical_base) &&
|
||||
(addr <= (mp->physical_base + mp->size - 1)))
|
||||
return(mp);
|
||||
}
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
void *
|
||||
mem_static_map_cached(uintptr_t ptr)
|
||||
{
|
||||
struct mem_static_map_entry *mp;
|
||||
ptrdiff_t offset;
|
||||
|
||||
mp = mem_static_find_virtual(ptr, &offset);
|
||||
if (NULL != mp)
|
||||
return((void *)(mp->cached_base + offset));
|
||||
return(MAP_FAILED);
|
||||
}
|
||||
|
||||
void *
|
||||
mem_static_map_uncached(uintptr_t ptr)
|
||||
{
|
||||
struct mem_static_map_entry *mp;
|
||||
ptrdiff_t offset;
|
||||
|
||||
mp = mem_static_find_virtual(ptr, &offset);
|
||||
if (NULL != mp)
|
||||
return((void *)(mp->uncached_base + offset));
|
||||
return(MAP_FAILED);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
mem_static_map_physical(uintptr_t ptr)
|
||||
{
|
||||
struct mem_static_map_entry *mp;
|
||||
ptrdiff_t offset;
|
||||
|
||||
mp = mem_static_find_virtual(ptr, &offset);
|
||||
if (NULL != mp)
|
||||
return(mp->physical_base + offset);
|
||||
return(MAP_NO_ENTRY);
|
||||
}
|
||||
+502
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2008 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <sys/menu.h>
|
||||
#include <sys/task.h>
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <drivers/power.h>
|
||||
#include <lib/env.h>
|
||||
|
||||
#if WITH_MENU
|
||||
#if !WITH_SIMPLE_MENU
|
||||
|
||||
// 0x1b 0x5b 0x41 uparrow
|
||||
// 0x1b 0x5b 0x42 downarrow
|
||||
// 0x1b 0x5b 0x43 rightarrow
|
||||
// 0x1b 0x5b 0x44 leftarrow
|
||||
|
||||
#define HISTORY_SIZE 16
|
||||
static char (*history)[256];
|
||||
static uint32_t curr_history_pos = 0;
|
||||
|
||||
#define kTokenBufSize (1024)
|
||||
#define kMaxMenuArgs 64
|
||||
|
||||
utime_t gMenuLastActivityTime;
|
||||
static char *token_buf;
|
||||
static char *console_prompt;
|
||||
|
||||
struct task_event gMenuTaskReadyEvent = EVENT_STATIC_INIT(gMenuTaskReadyEvent, false, 0);
|
||||
|
||||
static char *get_line(const char *prompt, char *buf, int buf_size)
|
||||
{
|
||||
int i;
|
||||
int escape;
|
||||
uint32_t history_pos = curr_history_pos;
|
||||
uint32_t saved_history_pos = curr_history_pos;
|
||||
|
||||
do_prompt:
|
||||
puts(prompt);
|
||||
|
||||
history[curr_history_pos][0] = 0;
|
||||
|
||||
i = 0;
|
||||
escape = 0;
|
||||
for (;;) {
|
||||
char c = getchar();
|
||||
|
||||
// printf("c = 0x%x (%c)\n", c, c);
|
||||
|
||||
/* Update activity time on any new input. */
|
||||
gMenuLastActivityTime = system_time();
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
escape = 0;
|
||||
putchar('\n');
|
||||
if (i != 0) {
|
||||
buf[i] = 0;
|
||||
|
||||
/* save the history, if it's different from the last command */
|
||||
if (strcmp(buf, history[(curr_history_pos - 1) % HISTORY_SIZE]) != 0) {
|
||||
strlcpy(history[curr_history_pos], buf, sizeof(history[curr_history_pos]));
|
||||
curr_history_pos = (curr_history_pos + 1) % HISTORY_SIZE;
|
||||
}
|
||||
return buf;
|
||||
} else {
|
||||
goto do_prompt;
|
||||
}
|
||||
break;
|
||||
case 0x8: // backspace
|
||||
case 0x7f: // del
|
||||
escape = 0;
|
||||
if (i > 0) {
|
||||
i--;
|
||||
putchar(0x8);
|
||||
putchar(0x20);
|
||||
putchar(0x8);
|
||||
}
|
||||
break;
|
||||
case 0x1b:
|
||||
escape = 1;
|
||||
break;
|
||||
case 0x5b:
|
||||
if (escape > 0)
|
||||
escape = 2;
|
||||
break;
|
||||
case 0x41: case 0x42:
|
||||
if (escape < 2) {
|
||||
goto regchar;
|
||||
} else {
|
||||
// up/down arrow
|
||||
char *new_history;
|
||||
|
||||
if (c == 0x41) {
|
||||
int temp = history_pos;
|
||||
history_pos = (history_pos - 1) % HISTORY_SIZE;
|
||||
if (history_pos == saved_history_pos) {
|
||||
// printf("%d %d\n", history_pos, saved_history_pos);
|
||||
// we've wrapped around
|
||||
history_pos = temp;
|
||||
break;
|
||||
}
|
||||
if (strlen(history[history_pos]) == 0) {
|
||||
// printf("%p %d, len 0\n", history[history_pos], history_pos);
|
||||
// we've walked before the start of history
|
||||
history_pos = temp;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (history_pos == saved_history_pos)
|
||||
break; // can't go forward past top of the history
|
||||
history_pos = (history_pos + 1) % HISTORY_SIZE;
|
||||
}
|
||||
new_history = history[history_pos];
|
||||
|
||||
/* wipe out the line */
|
||||
while(i > 0) {
|
||||
putchar(0x8);
|
||||
putchar(' ');
|
||||
putchar(0x8);
|
||||
i--;
|
||||
}
|
||||
|
||||
/* output the new line */
|
||||
for (i=0; new_history[i] != 0; i++) {
|
||||
buf[i] = new_history[i];
|
||||
putchar(new_history[i]);
|
||||
}
|
||||
}
|
||||
escape = 0;
|
||||
break;
|
||||
case 0x43: case 0x44:
|
||||
if (escape < 2) {
|
||||
goto regchar;
|
||||
} else {
|
||||
// left & right arrow
|
||||
escape = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
regchar:
|
||||
escape = 0;
|
||||
if (i < (buf_size - 1)) {
|
||||
buf[i++] = c;
|
||||
putchar(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* chop a string into multiple tokens, each pointed to by the tokens array.
|
||||
* if an unescaped ';' is hit, stop tokenizing and return the remainder of
|
||||
* the line in *line_remainder. If no ';' is hit, return null in *line_remainder.
|
||||
*/
|
||||
static int tokenize(const char *buf, char **tokens, int token_count, const char **line_remainder)
|
||||
{
|
||||
bool done;
|
||||
bool done_after;
|
||||
int inpos = 0;
|
||||
int outpos = 0;
|
||||
int outpos_token_start = 0;
|
||||
int token = 0;
|
||||
char last_char = 0;
|
||||
enum {
|
||||
INITIAL,
|
||||
CONSUME_WHITESPACE,
|
||||
TOKEN_START,
|
||||
TOKEN,
|
||||
QUOTED_TOKEN,
|
||||
FINISH_TOKEN,
|
||||
|
||||
#if WITH_ENV
|
||||
ENV_TOKEN,
|
||||
FINISH_ENV_TOKEN,
|
||||
#endif
|
||||
} state = INITIAL;
|
||||
|
||||
*line_remainder = NULL;
|
||||
|
||||
done = done_after = false;
|
||||
while (!done) {
|
||||
/* end of input buffer, finish off whatever token we're in the middle of and exit */
|
||||
if ((buf[inpos] == 0) || (outpos >= (kTokenBufSize - 1))) {
|
||||
done = true;
|
||||
if (state == TOKEN || state == QUOTED_TOKEN || state == FINISH_TOKEN)
|
||||
state = FINISH_TOKEN;
|
||||
#if WITH_ENV
|
||||
else if (state == ENV_TOKEN)
|
||||
state = FINISH_ENV_TOKEN;
|
||||
#endif
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (done_after)
|
||||
done = true;
|
||||
|
||||
// printf("tok: in %d (%c) out %d tok %d state %d\n",
|
||||
// inpos, buf[inpos], outpos, token, state);
|
||||
switch (state) {
|
||||
case INITIAL:
|
||||
case CONSUME_WHITESPACE:
|
||||
/* eat whitespace until we see the start of a token */
|
||||
if (isspace(buf[inpos])) {
|
||||
last_char = buf[inpos];
|
||||
inpos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
state = TOKEN_START;
|
||||
break;
|
||||
case TOKEN_START:
|
||||
/* start of a token, examine the first character to see if we need to do anything special */
|
||||
outpos_token_start = outpos;
|
||||
if (buf[inpos] == '#') {
|
||||
/* comment, ignore the rest of the line */
|
||||
last_char = buf[inpos];
|
||||
done = true;
|
||||
break;
|
||||
#if WITH_ENV
|
||||
} else if (buf[inpos] == '$') {
|
||||
/* environment variable */
|
||||
last_char = buf[inpos];
|
||||
inpos++;
|
||||
state = ENV_TOKEN;
|
||||
tokens[token] = &token_buf[outpos];
|
||||
#endif
|
||||
} else if (buf[inpos] == ';') {
|
||||
/* if we hit a unescaped ';', it's the end of the line */
|
||||
*line_remainder = &buf[inpos+1];
|
||||
done = true;
|
||||
break;
|
||||
} else if (buf[inpos] == '"') {
|
||||
/* quoted token */
|
||||
last_char = buf[inpos];
|
||||
inpos++;
|
||||
state = QUOTED_TOKEN;
|
||||
tokens[token] = &token_buf[outpos];
|
||||
} else {
|
||||
state = TOKEN;
|
||||
tokens[token] = &token_buf[outpos];
|
||||
}
|
||||
break;
|
||||
case TOKEN:
|
||||
/* consume characters until we hit whitespace */
|
||||
if (isspace(buf[inpos])) {
|
||||
/* whitespace, null terminate current token and switch to whitespace consume state */
|
||||
last_char = buf[inpos];
|
||||
state = FINISH_TOKEN;
|
||||
break;
|
||||
} else if (buf[inpos] == ';') {
|
||||
/* if we hit a unescaped ';', it's the end of the line */
|
||||
if (last_char != '\\') {
|
||||
*line_remainder = &buf[inpos+1];
|
||||
state = FINISH_TOKEN;
|
||||
done_after = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
last_char = buf[inpos];
|
||||
if (buf[inpos] != '\\')
|
||||
token_buf[outpos++] = buf[inpos];
|
||||
inpos++;
|
||||
break;
|
||||
case QUOTED_TOKEN:
|
||||
/* consume characters until we hit terminating quote */
|
||||
if (buf[inpos] == '"') {
|
||||
/* terminating quote, null terminate current token and switch to whitespace consume state */
|
||||
inpos++; // consume the quote
|
||||
state = FINISH_TOKEN;
|
||||
break;
|
||||
}
|
||||
|
||||
last_char = buf[inpos];
|
||||
token_buf[outpos++] = buf[inpos++];
|
||||
break;
|
||||
case FINISH_TOKEN:
|
||||
/* finish whatever token we're dealing with */
|
||||
token_buf[outpos++] = 0;
|
||||
if (++token < token_count)
|
||||
state = CONSUME_WHITESPACE;
|
||||
else {
|
||||
*line_remainder = &buf[inpos];
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
#if WITH_ENV
|
||||
case ENV_TOKEN:
|
||||
/* consume characters until we hit whitespace */
|
||||
if (isspace(buf[inpos])) {
|
||||
/* whitespace, we've now got the environment variable we want to read */
|
||||
last_char = buf[inpos];
|
||||
state = FINISH_ENV_TOKEN;
|
||||
break;
|
||||
}
|
||||
|
||||
last_char = buf[inpos];
|
||||
token_buf[outpos++] = buf[inpos++];
|
||||
break;
|
||||
case FINISH_ENV_TOKEN: {
|
||||
/* the current token now holds the environment variable we want to read */
|
||||
const char *env;
|
||||
|
||||
token_buf[outpos++] = 0;
|
||||
|
||||
/* read in the environment variable */
|
||||
env = env_get(tokens[token]);
|
||||
if (!env) {
|
||||
printf("\ncould not read environment var '%s'\n", tokens[token]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the value of the environment variable on top of the current token */
|
||||
strlcpy(&token_buf[outpos_token_start], env, kTokenBufSize - outpos_token_start);
|
||||
outpos = outpos_token_start + strlen(token_buf + outpos_token_start) + 1;
|
||||
|
||||
if (++token < token_count)
|
||||
state = CONSUME_WHITESPACE;
|
||||
else {
|
||||
*line_remainder = &buf[inpos];
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
panic("tokenize: got into an invalid state\n");
|
||||
}
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
bool process_command_line(char *line, int* ret_val)
|
||||
{
|
||||
char *tokens[kMaxMenuArgs];
|
||||
struct cmd_arg args[kMaxMenuArgs];
|
||||
int token_count;
|
||||
int ret = 0;
|
||||
const struct cmd_menu_item *menu_entry = NULL;
|
||||
void **menu_cursor;
|
||||
int argnum;
|
||||
|
||||
/* don't allow ourselves to be called before the menu task has started */
|
||||
if (NULL == token_buf)
|
||||
return(false);
|
||||
|
||||
/* Update activity; we might have come from outside the get_line() loop */
|
||||
gMenuLastActivityTime = system_time();
|
||||
|
||||
while(line) {
|
||||
memset(tokens, 0, sizeof(tokens));
|
||||
memset(args, 0, sizeof(args));
|
||||
token_count = tokenize(line, tokens, sizeof(tokens)/sizeof(char *), (const char **)&line);
|
||||
if (token_count <= 0)
|
||||
continue;
|
||||
|
||||
// printf("buf %p, line %p\n", buf, line);
|
||||
// for(i=0; i < token_count; i++)
|
||||
// printf("\t%d: '%s'\n", i, tokens[i]);
|
||||
|
||||
/* if the command starts with '?', only execute it if the last command returned a positive error */
|
||||
if (tokens[0][0] == '?') {
|
||||
tokens[0]++;
|
||||
if (ret < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
menu_entry = NULL;
|
||||
LINKER_SET_FOREACH(menu_cursor, menu) {
|
||||
menu_entry = (const struct cmd_menu_item *)*menu_cursor;
|
||||
if (!strcmp(menu_entry->cmd, tokens[0])) {
|
||||
|
||||
/* parse the arglist */
|
||||
for (argnum = 0; argnum < kMaxMenuArgs; argnum++) {
|
||||
/* are we done? */
|
||||
if (argnum >= token_count)
|
||||
break;
|
||||
|
||||
args[argnum].n = strtol(tokens[argnum], NULL, 0);
|
||||
args[argnum].u = strtoul(tokens[argnum], NULL, 0);
|
||||
args[argnum].h = strtol(tokens[argnum], NULL, 16);
|
||||
if (!strcmp(tokens[argnum], "true"))
|
||||
args[argnum].b = true;
|
||||
else if (!strcmp(tokens[argnum], "false"))
|
||||
args[argnum].b = false;
|
||||
else
|
||||
args[argnum].b = args[argnum].n ? true : false;
|
||||
args[argnum].str = tokens[argnum];
|
||||
}
|
||||
|
||||
/* call the routine */
|
||||
ret = menu_entry->func(argnum, args);
|
||||
if (ret_val)
|
||||
*ret_val = ret;
|
||||
#if WITH_ENV
|
||||
/* stick the result in an environment variable */
|
||||
env_set_uint("?", ret, 0);
|
||||
#endif
|
||||
break;
|
||||
} else {
|
||||
menu_entry = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (menu_entry == NULL);
|
||||
}
|
||||
|
||||
int menu_task(void *arg)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
/* allocate the history buffer */
|
||||
history = calloc(1, HISTORY_SIZE * sizeof(*history));
|
||||
|
||||
/* allocate the line buffer */
|
||||
buf = malloc(512);
|
||||
|
||||
/* allocate the token buffer */
|
||||
token_buf = malloc(kTokenBufSize);
|
||||
|
||||
/* signal others waiting for us */
|
||||
event_signal(&gMenuTaskReadyEvent);
|
||||
|
||||
menu_prompt(NULL);
|
||||
|
||||
for (;;) {
|
||||
/* prompt */
|
||||
puts("\x1b[m");
|
||||
get_line(console_prompt, buf, 512);
|
||||
|
||||
if (process_command_line(buf, NULL)) {
|
||||
printf("\x1b[1m?SYNTAX ERROR\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *menu_prompt(char *new_prompt)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
ret = console_prompt;
|
||||
if (NULL != new_prompt) {
|
||||
console_prompt = new_prompt;
|
||||
} else {
|
||||
console_prompt = "] ";
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static int do_help(int argc, struct cmd_arg *args)
|
||||
{
|
||||
const struct cmd_menu_item *menu_entry;
|
||||
void **menu_cursor;
|
||||
|
||||
printf("command list:\n");
|
||||
LINKER_SET_FOREACH(menu_cursor, menu) {
|
||||
menu_entry = (const struct cmd_menu_item *)*menu_cursor;
|
||||
if (menu_entry->help)
|
||||
printf(" %-16.16s %s\n", menu_entry->cmd, menu_entry->help);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MENU_COMMAND_DEVELOPMENT(help, do_help, NULL, NULL);
|
||||
|
||||
#if 0
|
||||
static int do_argtest(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("argtest, %d args:\n", argc);
|
||||
for (i=0; i<argc; i++) {
|
||||
printf("%d: n %d u %u h %x str %p (%s)\n", i, args[i].n, args[i].u, args[i].h, args[i].str, args[i].str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MENU_COMMAND_DEBUG(argtest, do_argtest, NULL, NULL);
|
||||
#endif
|
||||
|
||||
#endif /* !WITH_SIMPLE_MENU */
|
||||
#endif /* WITH_MENU */
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (C) 2014 Apple, Inc. All rights reserved.
|
||||
# Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple, Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple, Inc.
|
||||
#
|
||||
# generic sys stuff
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULES += \
|
||||
lib/cbuf
|
||||
|
||||
# Some of these files contain nothing if the corresponding WITH_XXX is not defined:
|
||||
#
|
||||
# menu.o WITH_MENU
|
||||
# simple_menu.o WITH_MENU
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/boot.o \
|
||||
$(LOCAL_DIR)/callout.o \
|
||||
$(LOCAL_DIR)/debug.o \
|
||||
$(LOCAL_DIR)/halt.o \
|
||||
$(LOCAL_DIR)/hash.o \
|
||||
$(LOCAL_DIR)/init.o \
|
||||
$(LOCAL_DIR)/lock.o \
|
||||
$(LOCAL_DIR)/mem.o \
|
||||
$(LOCAL_DIR)/menu.o \
|
||||
$(LOCAL_DIR)/simple_menu.o \
|
||||
$(LOCAL_DIR)/task.o \
|
||||
$(LOCAL_DIR)/time.o
|
||||
|
||||
# conditionally include the security module
|
||||
ifeq (${WITH_NO_SECURITY},true)
|
||||
OPTIONS += \
|
||||
WITH_NO_SECURITY=1
|
||||
|
||||
else
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/security.o
|
||||
endif
|
||||
+461
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2015 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <drivers/aes.h>
|
||||
#include <drivers/sha1.h>
|
||||
#include <lib/libc.h>
|
||||
#include <lib/devicetree.h>
|
||||
#include <lib/ramdisk.h>
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <sys/security.h>
|
||||
|
||||
#define RELEASE_SECURITY_MODE (kSecurityModeGIDKeyAccess | kSecurityModeUIDKeyAccess | kSecurityModeProdCertAccess | kSecurityStatusSystemTrusted)
|
||||
#define SECRET_SECURITY_MODE (RELEASE_SECURITY_MODE | kSecurityModeKDPEnabled)
|
||||
|
||||
// XXX Remove this once image4 transition for older platforms/targets is complete.
|
||||
#if !WITH_IMAGE4
|
||||
#define DEVELOPMENT_SECURITY_MODE (RELEASE_SECURITY_MODE | kSecurityModeExUntrust)
|
||||
#else
|
||||
#define DEVELOPMENT_SECURITY_MODE (RELEASE_SECURITY_MODE)
|
||||
#endif
|
||||
#define DEBUG_SECURITY_MODE (DEVELOPMENT_SECURITY_MODE | kSecurityModeKDPEnabled | kSecurityModeDebugCmd | kSecurityModeMemAccess | kSecurityModeHWAccess)
|
||||
|
||||
#if RELEASE_BUILD
|
||||
#define DEFAULT_SECURITY_MODE RELEASE_SECURITY_MODE
|
||||
#endif
|
||||
#if SECRET_BUILD
|
||||
#define DEFAULT_SECURITY_MODE SECRET_SECURITY_MODE
|
||||
#endif
|
||||
#if DEVELOPMENT_BUILD
|
||||
#define DEFAULT_SECURITY_MODE DEVELOPMENT_SECURITY_MODE
|
||||
#endif
|
||||
#if DEBUG_BUILD
|
||||
#define DEFAULT_SECURITY_MODE DEBUG_SECURITY_MODE
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_SECURITY_MODE
|
||||
#error Security: Unknown build style
|
||||
#endif
|
||||
|
||||
#define CLEAR_MEM_CHUNK_SIZE (256 * 1024 * 1024)
|
||||
|
||||
static security_mode_t security_mode;
|
||||
|
||||
static addr_t insecure_memory_start;
|
||||
static addr_t insecure_memory_end;
|
||||
|
||||
#if !DISABLE_CLEAR_MEMORY
|
||||
static bool secure_memory_cleared;
|
||||
#endif
|
||||
|
||||
#if WITH_IMAGE4
|
||||
static u_int8_t security_boot_manifest_hash[HASH_OUTPUT_SIZE];
|
||||
static bool security_environment_consolidated;
|
||||
#endif // WITH_IMAGE4
|
||||
|
||||
static void security_clear_mem_in_chunks(char *dst, size_t count);
|
||||
|
||||
int security_init(bool clear_memory)
|
||||
{
|
||||
security_mode = DEFAULT_SECURITY_MODE;
|
||||
|
||||
if (platform_get_secure_mode()) {
|
||||
security_mode |= kSecurityStatusSecureBoot;
|
||||
} else {
|
||||
#if APPLICATION_SECUREROM
|
||||
// SecureROM allows untrusted execution if secure_mode is false
|
||||
security_mode |= kSecurityModeExUntrust;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!platform_get_current_production_mode()) {
|
||||
security_mode |= kSecurityModeDevCertAccess;
|
||||
security_mode |= kSecurityModeKDPEnabled;
|
||||
}
|
||||
|
||||
if (platform_get_lock_fuses_required()) {
|
||||
security_mode |= kSecurityOptionLockFuses;
|
||||
}
|
||||
|
||||
insecure_memory_start = -1;
|
||||
insecure_memory_end = 0;
|
||||
|
||||
#if !DISABLE_CLEAR_MEMORY
|
||||
if (clear_memory) {
|
||||
/* Break up the memory region into chunks to keep watchdog alive in between the clearings */
|
||||
security_clear_mem_in_chunks((char *)INSECURE_MEMORY_BASE, INSECURE_MEMORY_SIZE);
|
||||
|
||||
#ifdef SECURE_MEMORY_SIZE
|
||||
if ((SECURE_MEMORY_SIZE != 0) && !secure_memory_cleared) {
|
||||
/* Break up the memory region into chunks to keep watchdog alive in between the clearings */
|
||||
security_clear_mem_in_chunks((char *)SECURE_MEMORY_BASE, SECURE_MEMORY_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
secure_memory_cleared = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
security_protect_memory((void *)INSECURE_MEMORY_BASE, INSECURE_MEMORY_SIZE, false);
|
||||
|
||||
#if WITH_RAMDISK
|
||||
ramdisk_init();
|
||||
#endif
|
||||
|
||||
#if WITH_DEVICETREE
|
||||
dt_init();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool security_allow_modes(security_mode_t modes)
|
||||
{
|
||||
bool result = (modes & security_mode) == modes;
|
||||
|
||||
if (result && ((modes & kSecurityModeExUntrust) != 0))
|
||||
security_set_untrusted();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool security_validate_image(security_image_t image_validity)
|
||||
{
|
||||
// Check if untrusted is allowed.
|
||||
if ((image_validity == kSecurityImageUntrusted) && !security_allow_modes(kSecurityModeExUntrust)) {
|
||||
image_validity = kSecurityImageInvalid;
|
||||
}
|
||||
|
||||
return image_validity != kSecurityImageInvalid;
|
||||
}
|
||||
|
||||
bool security_allow_memory(const void *address, size_t length)
|
||||
{
|
||||
addr_t addr = (addr_t)address, last = addr + length;
|
||||
bool allow = false;
|
||||
|
||||
if (security_allow_modes(kSecurityModeMemAccess))
|
||||
return true;
|
||||
|
||||
// Disallow wrap-around
|
||||
if (last <= addr)
|
||||
return false;
|
||||
|
||||
if ((insecure_memory_start <= addr) && (last <= insecure_memory_end))
|
||||
allow = true;
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
void security_protect_memory(const void *address, size_t length, bool protect)
|
||||
{
|
||||
addr_t addr = (addr_t)address, last = addr + length;
|
||||
|
||||
if (security_allow_modes(kSecurityModeMemAccess))
|
||||
return;
|
||||
|
||||
if (protect) {
|
||||
insecure_memory_start = -1;
|
||||
insecure_memory_end = 0;
|
||||
} else {
|
||||
if (addr < insecure_memory_start)
|
||||
insecure_memory_start = addr;
|
||||
if (insecure_memory_end < last)
|
||||
insecure_memory_end = last;
|
||||
}
|
||||
}
|
||||
|
||||
void security_enable_kdp(void)
|
||||
{
|
||||
if ((security_mode & kSecurityModeKDPEnabled) != 0)
|
||||
return;
|
||||
|
||||
security_allow_modes(kSecurityModeExUntrust);
|
||||
}
|
||||
|
||||
void security_set_untrusted(void)
|
||||
{
|
||||
security_mode &= ~(kSecurityModeGIDKeyAccess | kSecurityModeUIDKeyAccess | kSecurityStatusSystemTrusted);
|
||||
security_mode |= kSecurityModeKDPEnabled;
|
||||
}
|
||||
|
||||
void security_set_production_override(bool override)
|
||||
{
|
||||
security_mode &= ~kSecurityOptionClearProduction;
|
||||
if (override)security_mode |= kSecurityOptionClearProduction;
|
||||
}
|
||||
|
||||
bool security_get_production_override(void)
|
||||
{
|
||||
return (security_mode & kSecurityOptionClearProduction) != 0;
|
||||
}
|
||||
|
||||
#if WITH_IMAGE4
|
||||
|
||||
bool security_get_effective_production_status(bool with_demotion)
|
||||
{
|
||||
bool device_production_status = platform_get_current_production_mode();
|
||||
|
||||
return (device_production_status ? (device_production_status ^ with_demotion) : false);
|
||||
}
|
||||
|
||||
void security_set_boot_manifest_hash(u_int8_t *boot_manifest_hash)
|
||||
{
|
||||
security_mode &= ~kSecurityOptionBootManifestHashValid;
|
||||
|
||||
if (boot_manifest_hash == NULL) {
|
||||
security_mode &= ~kSecurityOptionBootManifestHashValid;
|
||||
} else {
|
||||
security_mode |= kSecurityOptionBootManifestHashValid;
|
||||
memcpy((void *)security_boot_manifest_hash, (const void *)boot_manifest_hash, sizeof(security_boot_manifest_hash));
|
||||
}
|
||||
}
|
||||
|
||||
bool security_get_boot_manifest_hash(u_int8_t *boot_manifest_hash, u_int32_t hash_length)
|
||||
{
|
||||
if (security_mode & kSecurityOptionBootManifestHashValid) {
|
||||
RELEASE_ASSERT(boot_manifest_hash != NULL);
|
||||
RELEASE_ASSERT(hash_length == sizeof(security_boot_manifest_hash));
|
||||
memcpy((void *)boot_manifest_hash, (const void *)security_boot_manifest_hash, hash_length);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void security_set_mix_n_match_prevention_status(bool mix_n_match_enforced)
|
||||
{
|
||||
security_mode &= ~kSecurityOptionMixNMatchPrevented;
|
||||
if (mix_n_match_enforced)
|
||||
security_mode |= kSecurityOptionMixNMatchPrevented;
|
||||
}
|
||||
|
||||
bool security_get_mix_n_match_prevention_status(void)
|
||||
{
|
||||
return (security_mode & kSecurityOptionMixNMatchPrevented);
|
||||
}
|
||||
|
||||
void security_set_lock_fuses(void)
|
||||
{
|
||||
security_mode |= kSecurityOptionLockFuses;
|
||||
}
|
||||
|
||||
bool security_get_lock_fuses(void)
|
||||
{
|
||||
return (security_mode & kSecurityOptionLockFuses);
|
||||
}
|
||||
|
||||
bool security_restore_environment(void)
|
||||
{
|
||||
uint8_t boot_manifest_hash[HASH_OUTPUT_SIZE];
|
||||
|
||||
if (security_environment_consolidated)
|
||||
return false;
|
||||
|
||||
security_set_mix_n_match_prevention_status(platform_get_mix_n_match_prevention_status());
|
||||
|
||||
if (platform_get_boot_manifest_hash((u_int8_t *)boot_manifest_hash) == 0)
|
||||
security_set_boot_manifest_hash((u_int8_t *)boot_manifest_hash);
|
||||
else
|
||||
security_set_boot_manifest_hash(NULL);
|
||||
|
||||
if (security_get_production_override())
|
||||
security_set_production_override(false);
|
||||
|
||||
if ((security_mode & (kSecurityModeGIDKeyAccess | kSecurityModeUIDKeyAccess | kSecurityStatusSystemTrusted)) == 0)
|
||||
security_mode |= (kSecurityModeGIDKeyAccess | kSecurityModeUIDKeyAccess | kSecurityStatusSystemTrusted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool security_consolidate_environment(void)
|
||||
{
|
||||
/* demote production status if requested */
|
||||
if (security_get_production_override())
|
||||
platform_demote_production();
|
||||
|
||||
/* disable all keys not requested */
|
||||
if (!security_allow_modes(kSecurityModeGIDKeyAccess))
|
||||
platform_disable_keys(~0, 0);
|
||||
if (!security_allow_modes(kSecurityModeUIDKeyAccess))
|
||||
platform_disable_keys(0, ~0);
|
||||
|
||||
/* update next stage boot-manifest-hash, and related flags */
|
||||
if (security_mode & kSecurityOptionBootManifestHashValid)
|
||||
platform_set_boot_manifest_hash((u_int8_t *)security_boot_manifest_hash);
|
||||
else
|
||||
platform_set_boot_manifest_hash(NULL);
|
||||
|
||||
platform_set_mix_n_match_prevention_status(((security_mode & kSecurityOptionMixNMatchPrevented) == kSecurityOptionMixNMatchPrevented));
|
||||
|
||||
/* record environment was consolidated atleast once */
|
||||
security_environment_consolidated = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else // WITH_IMAGE4
|
||||
|
||||
bool security_consolidate_environment(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // WITH_IMAGE4
|
||||
|
||||
static const char gSleepToken[] = "iBootSleepValid";
|
||||
|
||||
void security_create_sleep_token(addr_t address)
|
||||
{
|
||||
uint8_t key_buffer[16];
|
||||
|
||||
memcpy((void *)address, gSleepToken, sizeof(gSleepToken));
|
||||
|
||||
#if WITH_AES
|
||||
if (!security_allow_modes(kSecurityModeUIDKeyAccess))
|
||||
return;
|
||||
|
||||
memcpy(key_buffer, gSleepToken, sizeof(gSleepToken));
|
||||
|
||||
/* note that we ignore any errors from AES here */
|
||||
aes_cbc_encrypt(key_buffer, key_buffer, sizeof(key_buffer), AES_KEY_TYPE_UID0, NULL, NULL);
|
||||
aes_cbc_encrypt((u_int8_t *)address, (u_int8_t *)address, sizeof(gSleepToken), AES_KEY_TYPE_USER, key_buffer, NULL);
|
||||
#endif
|
||||
memset(key_buffer, 0, sizeof(key_buffer));
|
||||
}
|
||||
|
||||
bool security_validate_sleep_token(addr_t address)
|
||||
{
|
||||
uint8_t key_buffer[16];
|
||||
uint8_t token_buffer[16];
|
||||
bool result = false;
|
||||
|
||||
#if WITH_AES
|
||||
if (security_allow_modes(kSecurityModeUIDKeyAccess)) {
|
||||
memcpy(token_buffer, gSleepToken, sizeof(token_buffer));
|
||||
|
||||
memcpy(key_buffer, gSleepToken, sizeof(key_buffer));
|
||||
|
||||
/* note that we ignore any errors from AES here */
|
||||
aes_cbc_encrypt(key_buffer, key_buffer, sizeof(key_buffer), AES_KEY_TYPE_UID0, NULL, NULL);
|
||||
aes_cbc_encrypt(token_buffer, token_buffer, sizeof(token_buffer), AES_KEY_TYPE_USER, key_buffer, NULL);
|
||||
|
||||
if (!memcmp_secure((void *)address, token_buffer, sizeof(token_buffer))) {
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!memcmp_secure((void *)address, gSleepToken, sizeof(gSleepToken)) && security_allow_modes(kSecurityModeExUntrust)) {
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
memset(key_buffer, 0, sizeof(key_buffer));
|
||||
memset(token_buffer, 0, sizeof(token_buffer));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function avoids clearing memory in one bzero call, since that might
|
||||
* take too long such that watchdog fires (if enabled) and causes a full system reset
|
||||
*/
|
||||
static void security_clear_mem_in_chunks(char *dst, size_t count)
|
||||
{
|
||||
#if APPLICATION_SECUREROM && WITH_DMA_CLEAR
|
||||
extern void platform_clear_mem_with_dma(char *dst, size_t count);
|
||||
platform_clear_mem_with_dma(dst, count);
|
||||
#else // !WITH_DMA_CLEAR || !APPLICATION_SECUREROM
|
||||
size_t chunk_len;
|
||||
do {
|
||||
/* reset watchdog timer in case it's enabled */
|
||||
platform_watchdog_tickle();
|
||||
|
||||
/* Only clear CLEAR_MEM_CHUNK_SIZE bytes at a time */
|
||||
chunk_len = (count > CLEAR_MEM_CHUNK_SIZE) ? CLEAR_MEM_CHUNK_SIZE : count;
|
||||
bzero((void *)dst, chunk_len);
|
||||
dst += chunk_len;
|
||||
count -= chunk_len;
|
||||
}while (count > 0);
|
||||
#endif // WITH_DMA_CLEAR && APPLICATION_SECUREROM
|
||||
}
|
||||
|
||||
#if WITH_SIDP
|
||||
#if APPLICATION_SECUREROM
|
||||
|
||||
void security_sidp_seal_rom_manifest(void)
|
||||
{
|
||||
// Is the boot manifest hash valid?
|
||||
if (security_mode & kSecurityOptionBootManifestHashValid) {
|
||||
// Copy the manifest hash to a word aligned buffer.
|
||||
uint32_t manifest_buffer[HASH_OUTPUT_SIZE / sizeof(uint32_t)];
|
||||
memcpy(manifest_buffer, security_boot_manifest_hash, HASH_OUTPUT_SIZE);
|
||||
|
||||
// Copy the manifest hash to ROM seal registers.
|
||||
platform_sidp_set_rom_manifest(manifest_buffer, HASH_OUTPUT_SIZE);
|
||||
} else {
|
||||
// Clear the ROM seal registers.
|
||||
platform_sidp_set_rom_manifest(NULL, 0);
|
||||
}
|
||||
|
||||
// Lock the ROM seal.
|
||||
platform_sidp_lock_rom_manifest();
|
||||
}
|
||||
|
||||
#else // !APPLICATION_SECUREROM
|
||||
|
||||
void security_sidp_seal_boot_manifest(bool require_unlocked)
|
||||
{
|
||||
// The caller must have consolidated the security environment before
|
||||
// calling this function.
|
||||
RELEASE_ASSERT(security_environment_consolidated);
|
||||
|
||||
// Did a prior boot stage lock the manifest?
|
||||
if (platform_sidp_boot_manifest_locked()) {
|
||||
if (require_unlocked) {
|
||||
panic("Boot manifest already locked");
|
||||
}
|
||||
|
||||
// Yep, nothing to do.
|
||||
dprintf(DEBUG_INFO, "SiDP boot manifest already locked\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Is the boot manifest hash valid?
|
||||
if (security_mode & kSecurityOptionBootManifestHashValid) {
|
||||
// Copy the manifest hash to a word aligned buffer.
|
||||
uint32_t manifest_buffer[HASH_OUTPUT_SIZE / sizeof(uint32_t)];
|
||||
memcpy(manifest_buffer, security_boot_manifest_hash, HASH_OUTPUT_SIZE);
|
||||
|
||||
// Copy the manifest hash to the bootloader seal registers.
|
||||
platform_sidp_set_boot_manifest(manifest_buffer, HASH_OUTPUT_SIZE);
|
||||
} else {
|
||||
// Clear the bootloader seal registers.
|
||||
platform_sidp_set_boot_manifest(NULL, 0);
|
||||
}
|
||||
|
||||
// Mix-n-match allowed?
|
||||
if (!security_get_mix_n_match_prevention_status()) {
|
||||
platform_sidp_set_mix_n_match();
|
||||
}
|
||||
|
||||
// Lock the boot manifest.
|
||||
platform_sidp_lock_boot_manifest();
|
||||
}
|
||||
|
||||
#endif // APPLICATION_SECUREROM
|
||||
#endif // WITH_SIDP
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Computer, Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Computer, Inc.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
#include <sys.h>
|
||||
#include <sys/menu.h>
|
||||
#include <sys/task.h>
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <drivers/power.h>
|
||||
#include <lib/env.h>
|
||||
|
||||
#if WITH_MENU
|
||||
#if WITH_SIMPLE_MENU
|
||||
|
||||
#define kTokenBufSize 128
|
||||
#define kMaxTokens 8
|
||||
|
||||
utime_t gMenuLastActivityTime;
|
||||
static char *token_buf;
|
||||
|
||||
struct task_event gMenuTaskReadyEvent = EVENT_STATIC_INIT(gMenuTaskReadyEvent, false, 0);
|
||||
|
||||
/*
|
||||
* Parse tokens from (buf).
|
||||
*/
|
||||
static int
|
||||
tokenize(const char *buf, char **tokens, int token_count)
|
||||
{
|
||||
const char *in;
|
||||
char *out, *out_limit;
|
||||
int token;
|
||||
enum {
|
||||
WHITESPACE,
|
||||
TOKEN,
|
||||
DONE
|
||||
} state;
|
||||
|
||||
in = buf;
|
||||
out = token_buf;
|
||||
out_limit = out + kTokenBufSize - 1;
|
||||
token = 0;
|
||||
state = WHITESPACE;
|
||||
|
||||
for (;;) {
|
||||
/* state machine */
|
||||
switch(state) {
|
||||
case WHITESPACE:
|
||||
/* if we hit the end of the input, bail now */
|
||||
if ('\0' == *in) {
|
||||
state = DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* found a token starting? */
|
||||
if (!isspace(*in)) {
|
||||
tokens[token] = out;
|
||||
state = TOKEN;
|
||||
break;
|
||||
}
|
||||
/* discard and move on */
|
||||
in++;
|
||||
break;
|
||||
|
||||
case TOKEN:
|
||||
/* output cursor has reached or exceeded the last safe character */
|
||||
if (out >= out_limit) {
|
||||
/* terminate at the last safe character */
|
||||
*out_limit = '\0';
|
||||
/* update the token count */
|
||||
token++;
|
||||
/* and we are done */
|
||||
state = DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* input cursor has reached the end of the input or whitespace */
|
||||
if (('\0' == *in) || isspace(*in)) {
|
||||
*out++ = '\0';
|
||||
token++;
|
||||
|
||||
/* if the input is exhausted or we have run out of token space */
|
||||
if (('\0' == *in) || (token >= token_count)) {
|
||||
state = DONE;
|
||||
} else {
|
||||
/* return to discarding whitespace */
|
||||
state = WHITESPACE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* copy another character */
|
||||
*out++ = *in++;
|
||||
break;
|
||||
|
||||
case DONE:
|
||||
return(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
process_command_line(char *line, int *ret_val)
|
||||
{
|
||||
char *tokens[kMaxTokens];
|
||||
struct cmd_arg args[kMaxTokens];
|
||||
const struct cmd_menu_item *menu_entry;
|
||||
void **menu_cursor;
|
||||
int argnum;
|
||||
int token_count;
|
||||
int ret;
|
||||
|
||||
/* don't allow ourselves to be called before the menu task has started */
|
||||
if (NULL == token_buf)
|
||||
return(true);
|
||||
|
||||
/* Update activity; we might have come from outside the get_line() loop */
|
||||
gMenuLastActivityTime = system_time();
|
||||
|
||||
/* tokenize, will never return > kMaxTokens */
|
||||
token_count = tokenize(line, tokens, kMaxTokens);
|
||||
|
||||
if (token_count > 0) {
|
||||
LINKER_SET_FOREACH(menu_cursor, menu) {
|
||||
menu_entry = (const struct cmd_menu_item *)*menu_cursor;
|
||||
if (!strcmp(menu_entry->cmd, tokens[0])) {
|
||||
|
||||
/* parse the arglist */
|
||||
for (argnum = 0; argnum < token_count; argnum++) {
|
||||
/* are we done? */
|
||||
if (argnum >= token_count)
|
||||
break;
|
||||
|
||||
args[argnum].n = strtol(tokens[argnum], NULL, 0);
|
||||
args[argnum].u = strtoul(tokens[argnum], NULL, 0);
|
||||
args[argnum].h = strtol(tokens[argnum], NULL, 16);
|
||||
if (!strcmp(tokens[argnum], "true"))
|
||||
args[argnum].b = true;
|
||||
else if (!strcmp(tokens[argnum], "false"))
|
||||
args[argnum].b = false;
|
||||
else
|
||||
args[argnum].b = args[argnum].n ? true : false;
|
||||
args[argnum].str = tokens[argnum];
|
||||
}
|
||||
|
||||
/* call the routine */
|
||||
ret = menu_entry->func(argnum, args);
|
||||
if (ret_val)
|
||||
*ret_val = ret;
|
||||
#if WITH_ENV
|
||||
/* stick the result in an environment variable */
|
||||
env_set_uint("?", ret, 0);
|
||||
#endif
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
int menu_task(void *arg)
|
||||
{
|
||||
|
||||
/* allocate the token buffer */
|
||||
token_buf = malloc(kTokenBufSize);
|
||||
|
||||
/* signal others waiting for us */
|
||||
event_signal(&gMenuTaskReadyEvent);
|
||||
|
||||
/* spin throwing away console input */
|
||||
for (;;) {
|
||||
|
||||
/* get an input character */
|
||||
(void)getchar();
|
||||
|
||||
/* Update activity time on any new input. */
|
||||
gMenuLastActivityTime = system_time();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WITH_SIMPLE_MENU */
|
||||
#endif /* WITH_MENU */
|
||||
+774
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <list.h>
|
||||
#include <debug.h>
|
||||
#include <string.h>
|
||||
#include <arch.h>
|
||||
#include <platform.h>
|
||||
#include <lib/heap.h>
|
||||
#include <lib/mib.h>
|
||||
#include <lib/random.h>
|
||||
#include <sys.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/task.h>
|
||||
#include <sys/menu.h>
|
||||
#include <platform/timer.h>
|
||||
|
||||
#if !WITH_HW_TIMER
|
||||
# error Cannot build task subsystem without timer support
|
||||
#endif
|
||||
|
||||
#define DEFAULT_TASK_STACK_MAGIC 'stak'
|
||||
|
||||
/* list of all tasks */
|
||||
static struct list_node task_list = LIST_INITIAL_VALUE(task_list);
|
||||
static int task_count;
|
||||
|
||||
static int max_task_id;
|
||||
|
||||
/* global run queue */
|
||||
static struct list_node run_q = LIST_INITIAL_VALUE(run_q);
|
||||
static volatile int run_q_len;
|
||||
|
||||
/* initial bootstrapping task, partially constructed */
|
||||
static struct task bootstrap_task = {
|
||||
.magic = TASK_MAGIC,
|
||||
.state = TASK_RUNNING,
|
||||
.irq_disable_count = 1,
|
||||
.routine = NULL,
|
||||
.arg = NULL,
|
||||
.stack_base = NULL,
|
||||
.stack_len = 0,
|
||||
|
||||
.name = "bootstrap",
|
||||
.magic2 = TASK_MAGIC2,
|
||||
.task_id = 0,
|
||||
};
|
||||
|
||||
/* idle task */
|
||||
static int idle_task_routine(void *);
|
||||
|
||||
#ifndef IDLE_TASK_SIZE
|
||||
#define IDLE_TASK_SIZE TASK_STACK_MIN
|
||||
#endif
|
||||
|
||||
static uint8_t idle_task_stack[IDLE_TASK_SIZE] __attribute__ ((aligned(16)));
|
||||
static struct task idle_task;
|
||||
static uint64_t idle_ticks = 0ULL;
|
||||
static uint64_t deep_idle_ticks = 0ULL;
|
||||
static uint64_t boot_time_ticks;
|
||||
static uint64_t shallow_idle_cnt = 0ULL;
|
||||
static uint64_t deep_idle_cnt = 0ULL;
|
||||
static uint64_t last_yield;
|
||||
static uint64_t last_switch;
|
||||
static uint32_t task_stack_magic = DEFAULT_TASK_STACK_MAGIC;
|
||||
|
||||
MIB_VARIABLE(kMIBSystemBootTime, kOIDTypeUInt64, boot_time_ticks);
|
||||
MIB_VARIABLE(kMIBSystemIdleTime, kOIDTypeUInt64, idle_ticks);
|
||||
|
||||
static void
|
||||
uptime_handler(u_int32_t oid __unused, void *arg __unused, void *data)
|
||||
{
|
||||
u_int64_t *resptr = (u_int64_t *)data;
|
||||
u_int64_t now;
|
||||
|
||||
now = timer_get_ticks();
|
||||
*resptr = now - boot_time_ticks;
|
||||
}
|
||||
|
||||
MIB_FUNCTION(kMIBSystemUptime, kOIDTypeUInt64, uptime_handler, NULL);
|
||||
|
||||
/* currently running task */
|
||||
struct task *current_task = &bootstrap_task;
|
||||
|
||||
#if defined(__arm__)
|
||||
static uint32_t nullptr_value;
|
||||
#endif
|
||||
static bool deep_idle;
|
||||
|
||||
#ifndef DEEP_IDLE_THRESHOLD_US
|
||||
#define DEEP_IDLE_THRESHOLD_US 1000
|
||||
#endif
|
||||
|
||||
static volatile uint32_t deep_idle_threshold_us = 0; // <rdar://problem/8913103>
|
||||
|
||||
static struct task *task_create_etc(struct task *t, const char *name, task_routine routine, void *arg, void *stack, size_t stack_len);
|
||||
static void insert_run_q_head(struct task *t);
|
||||
static void insert_run_q_tail(struct task *t);
|
||||
|
||||
struct callout deep_idle_callout;
|
||||
static void deep_idle_timeout(struct callout *co, void *arg)
|
||||
{
|
||||
deep_idle = true;
|
||||
}
|
||||
|
||||
static int idle_task_routine(void *arg)
|
||||
{
|
||||
u_int64_t idle_enter, idle_leave;
|
||||
u_int64_t *ticks_accumulator;
|
||||
|
||||
for(;;) {
|
||||
task_yield();
|
||||
idle_enter = timer_get_ticks();
|
||||
|
||||
enter_critical_section();
|
||||
ticks_accumulator = & idle_ticks;
|
||||
if (likely(run_q_len == 0)) {
|
||||
if (deep_idle) {
|
||||
ticks_accumulator = & deep_idle_ticks;
|
||||
deep_idle_cnt++;
|
||||
platform_deep_idle();
|
||||
deep_idle = false;
|
||||
} else {
|
||||
/* Setup the "deep idle" callout and halt ("shallow idle") */
|
||||
if (deep_idle_threshold_us)
|
||||
callout_enqueue(&deep_idle_callout, deep_idle_threshold_us, deep_idle_timeout, NULL);
|
||||
ticks_accumulator = & idle_ticks;
|
||||
shallow_idle_cnt++;
|
||||
platform_halt();
|
||||
}
|
||||
}
|
||||
exit_critical_section();
|
||||
|
||||
/* We should never be here with interrupts disabled */
|
||||
RELEASE_ASSERT(0 == current_task->irq_disable_count);
|
||||
|
||||
idle_leave = timer_get_ticks();
|
||||
*ticks_accumulator += idle_leave - idle_enter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void task_init_stack_magic_cookie(void)
|
||||
{
|
||||
#if WITH_RANDOM
|
||||
uint8_t *guard = (uint8_t*)&task_stack_magic;
|
||||
|
||||
if (random_get_bytes((u_int8_t *)&task_stack_magic, sizeof(task_stack_magic)) != 0) {
|
||||
// In case of failure, put back the static cookie just in case.
|
||||
task_stack_magic = DEFAULT_TASK_STACK_MAGIC;
|
||||
}
|
||||
|
||||
guard[task_stack_magic & (sizeof(task_stack_magic) - 1)] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void task_init(void)
|
||||
{
|
||||
|
||||
dprintf(DEBUG_INFO, "initializing task system\n");
|
||||
|
||||
boot_time_ticks = timer_get_ticks();
|
||||
|
||||
#if !WITH_NO_RANDOM_STACK_COOKIE
|
||||
/* initialize the random cookie used for overflow detection */
|
||||
task_init_stack_magic_cookie();
|
||||
#endif
|
||||
|
||||
/* go back and fill in some more details of the bootstrap task */
|
||||
wait_queue_init(&bootstrap_task.return_waiters);
|
||||
|
||||
/* construct an idle task */
|
||||
task_create_etc(&idle_task, "idle task", &idle_task_routine, NULL, idle_task_stack, sizeof(idle_task_stack));
|
||||
idle_task.state = TASK_READY; /* it never gets started properly */
|
||||
|
||||
task_count = 2; /* bootstrap task, idle task */
|
||||
|
||||
#if defined(__arm__) && !defined(__clang_analyzer__)
|
||||
nullptr_value = *(volatile uint32_t *)NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct task *task_get_current_task(void)
|
||||
{
|
||||
return(current_task);
|
||||
}
|
||||
|
||||
static struct task *task_create_etc(struct task *t, const char *name, task_routine routine, void *arg, void *stack, size_t stack_len)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
/* clear the storage for the task */
|
||||
memset(t, 0, sizeof(struct task));
|
||||
|
||||
/* set up the new task */
|
||||
t->magic = TASK_MAGIC;
|
||||
t->magic2 = TASK_MAGIC2;
|
||||
t->stack_base = stack;
|
||||
t->stack_len = stack_len;
|
||||
strlcpy(t->name, name, sizeof(t->name));
|
||||
t->routine = routine;
|
||||
t->arg = arg;
|
||||
|
||||
enter_critical_section();
|
||||
t->task_id = ++max_task_id;
|
||||
exit_critical_section();
|
||||
|
||||
list_clear_node(&t->queue_node);
|
||||
wait_queue_init(&t->return_waiters);
|
||||
|
||||
t->state = TASK_INITIAL;
|
||||
t->irq_disable_count = 1; /* start off as if we were inside a critical section */
|
||||
|
||||
/* fill the stack with 'magic' */
|
||||
for (i = 0; i < (stack_len / sizeof(uint32_t)); i++)
|
||||
*((uint32_t *)t->stack_base + i) = task_stack_magic;
|
||||
|
||||
arch_task_create(t);
|
||||
|
||||
/* add ourselves to the global task list */
|
||||
list_add_head(&task_list, &t->task_list_node);
|
||||
|
||||
task_count++;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
struct task *task_create(const char *name, task_routine routine, void *arg, size_t stack_len)
|
||||
{
|
||||
struct task *t;
|
||||
void *stack;
|
||||
|
||||
/* stacks must be at least TASK_STACK_MIN bytes */
|
||||
if (stack_len < TASK_STACK_MIN)
|
||||
stack_len = TASK_STACK_MIN;
|
||||
|
||||
/* it will panic if not to be able to allocate stack */
|
||||
t = malloc(sizeof(struct task));
|
||||
/* assumption: malloc always returns cache-line size aligned buffer */
|
||||
stack = malloc(stack_len);
|
||||
|
||||
return task_create_etc(t, name, routine, arg, stack, stack_len);
|
||||
}
|
||||
|
||||
void task_destroy(struct task *t)
|
||||
{
|
||||
list_delete(&t->task_list_node);
|
||||
free(t->stack_base);
|
||||
free(t);
|
||||
}
|
||||
|
||||
static void insert_run_q_head(struct task *t)
|
||||
{
|
||||
ASSERT(t != &idle_task);
|
||||
ASSERT(run_q_len < task_count);
|
||||
|
||||
list_add_head(&run_q, &t->queue_node);
|
||||
run_q_len++;
|
||||
}
|
||||
|
||||
static void insert_run_q_tail(struct task *t)
|
||||
{
|
||||
ASSERT(t != &idle_task);
|
||||
ASSERT(run_q_len < task_count);
|
||||
|
||||
list_add_tail(&run_q, &t->queue_node);
|
||||
run_q_len++;
|
||||
}
|
||||
|
||||
static struct task *pop_run_q_head(void)
|
||||
{
|
||||
struct task *t = list_remove_head_type(&run_q, struct task, queue_node);
|
||||
if (t) {
|
||||
ASSERT(run_q_len > 0);
|
||||
run_q_len--;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
void task_yield(void)
|
||||
{
|
||||
struct task *old_task;
|
||||
struct task *new_task;
|
||||
|
||||
last_yield = timer_get_ticks();
|
||||
|
||||
/*
|
||||
* If the current task is still ready to run and there's no other tasks ready, just
|
||||
* continue.
|
||||
* Note that this is simply an optimisation to avoid entering the critical section;
|
||||
* we will later check again whether the current task is runnable to avoid racing
|
||||
* with wakeups.
|
||||
*/
|
||||
if (likely(run_q_len == 0) && likely(current_task->state == TASK_RUNNING))
|
||||
return;
|
||||
|
||||
/*
|
||||
* It's not safe to yield in panic context.
|
||||
*/
|
||||
ASSERT(NULL == gPanicStr);
|
||||
|
||||
/*
|
||||
* Sanity check the world before we yield from this task.
|
||||
*
|
||||
* XXX we could avoid this for the idle task to improve latency for
|
||||
* tasks made runnable in interrupt context.
|
||||
*/
|
||||
if (likely(&bootstrap_task != current_task)) {
|
||||
#if defined(__arm__) && !defined(__clang_analyzer__)
|
||||
/* the bootstrap task might yield before calling task_init() */
|
||||
/* XXX really? */
|
||||
if (unlikely(nullptr_value != *(volatile uint32_t *)NULL))
|
||||
panic("reset vector overwritten while executing task '%s' (0x%08x)",
|
||||
current_task->name, *(volatile uint32_t *)NULL);
|
||||
#endif
|
||||
|
||||
/* we don't know the base of the bootstrap stack, so it has no magic */
|
||||
if (unlikely(task_stack_magic != *(uint32_t *)current_task->stack_base))
|
||||
panic("task '%s' has overflowed its stack", current_task->name);
|
||||
}
|
||||
|
||||
enter_critical_section();
|
||||
|
||||
/*
|
||||
* We're headed to another task, so cancel the deep idle callout. Clear the
|
||||
* flag too, just in case.
|
||||
*/
|
||||
callout_dequeue(&deep_idle_callout);
|
||||
deep_idle = false;
|
||||
|
||||
/* if it's blocked or finished, it shouldn't go back to the run queue */
|
||||
old_task = current_task;
|
||||
if (old_task->state == TASK_RUNNING) {
|
||||
old_task->state = TASK_READY;
|
||||
/* the idle task gets scheduled when the run_q is empty; don't put it in there */
|
||||
if (old_task != &idle_task)
|
||||
insert_run_q_tail(old_task);
|
||||
}
|
||||
|
||||
/* find the next task */
|
||||
new_task = pop_run_q_head();
|
||||
if (new_task == NULL)
|
||||
new_task = &idle_task;
|
||||
|
||||
#if 0
|
||||
dprintf(DEBUG_SPEW, "task_yield: switching from task %p (%s) %d %p to task %p (%s) %d %p\n",
|
||||
old_task, old_task->name, old_task->irq_disable_count, old_task->stack_base,
|
||||
new_task, new_task->name, new_task->irq_disable_count, new_task->stack_base);
|
||||
#endif
|
||||
|
||||
ASSERT(new_task != old_task);
|
||||
|
||||
new_task->state = TASK_RUNNING;
|
||||
current_task = new_task;
|
||||
|
||||
/* XXX we could sanity-check the new task here before switching to it */
|
||||
arch_task_context_switch(&old_task->arch, &new_task->arch);
|
||||
|
||||
last_switch = timer_get_ticks();
|
||||
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
void task_start(struct task *t)
|
||||
{
|
||||
ASSERT(NULL != t);
|
||||
|
||||
if (t->state == TASK_INITIAL) {
|
||||
/* stick it in the run queue */
|
||||
t->state = TASK_READY;
|
||||
enter_critical_section();
|
||||
insert_run_q_tail(t);
|
||||
exit_critical_section();
|
||||
}
|
||||
}
|
||||
|
||||
void task_exit(int return_code)
|
||||
{
|
||||
/* set our return code and wake anyone waiting on us */
|
||||
current_task->return_code = return_code;
|
||||
current_task->state = TASK_FINISHED;
|
||||
wait_queue_wake_all(¤t_task->return_waiters);
|
||||
|
||||
task_count--;
|
||||
task_yield();
|
||||
|
||||
/* should never reach here */
|
||||
panic("task_exit: should not be here\n");
|
||||
}
|
||||
|
||||
int task_wait_on(struct task *t)
|
||||
{
|
||||
ASSERT(NULL != t);
|
||||
|
||||
if (t->state == TASK_FINISHED)
|
||||
return t->return_code;
|
||||
|
||||
/* block on the task's wait queue and return with it's return code */
|
||||
// XXX race condition here for someone deleting the finished task before a
|
||||
// waiter gets to read it's return code
|
||||
task_block(&t->return_waiters);
|
||||
ASSERT(t->state == TASK_FINISHED);
|
||||
|
||||
return t->return_code;
|
||||
}
|
||||
|
||||
void task_block(struct task_wait_queue *q)
|
||||
{
|
||||
/* stick ourselves into the wait queue, set our state to BLOCKED, and yield */
|
||||
enter_critical_section();
|
||||
current_task->state = TASK_BLOCKED;
|
||||
list_add_tail(&q->task_list, ¤t_task->queue_node);
|
||||
|
||||
task_yield();
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
/* happens at interrupt context */
|
||||
static void task_sleep_callback(struct callout *c, void *_task)
|
||||
{
|
||||
struct task *t = (struct task *)_task;
|
||||
|
||||
ASSERT(c == &t->sleep_callout);
|
||||
ASSERT(t->state == TASK_SLEEPING);
|
||||
|
||||
t->state = TASK_READY;
|
||||
insert_run_q_tail(t);
|
||||
}
|
||||
|
||||
void task_sleep(utime_t delay)
|
||||
{
|
||||
/* queue our sleep callout and reschedule */
|
||||
enter_critical_section();
|
||||
current_task->state = TASK_SLEEPING;
|
||||
callout_enqueue(¤t_task->sleep_callout, delay, &task_sleep_callback, current_task);
|
||||
|
||||
task_yield();
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
/* wait queue stuff */
|
||||
|
||||
void wait_queue_init(struct task_wait_queue *q)
|
||||
{
|
||||
ASSERT(NULL != q);
|
||||
|
||||
list_initialize(&q->task_list);
|
||||
}
|
||||
|
||||
void wait_queue_destroy(struct task_wait_queue *q)
|
||||
{
|
||||
wait_queue_wake_all(q);
|
||||
}
|
||||
|
||||
int wait_queue_wake_all(struct task_wait_queue *q)
|
||||
{
|
||||
struct task *t;
|
||||
struct task *temp;
|
||||
int ret = 0;
|
||||
|
||||
/* iterate through the wait queue, sticking each of the waiting threads in the run queue */
|
||||
enter_critical_section();
|
||||
list_for_every_entry_safe(&q->task_list, t, temp, struct task, queue_node) {
|
||||
list_delete(&t->queue_node);
|
||||
|
||||
ASSERT(t->state == TASK_BLOCKED);
|
||||
t->state = TASK_READY;
|
||||
insert_run_q_tail(t);
|
||||
ret++;
|
||||
}
|
||||
exit_critical_section();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wait_queue_wake_one(struct task_wait_queue *q)
|
||||
{
|
||||
struct task *t;
|
||||
int ret = 0;
|
||||
|
||||
/* pop the head of the wait queue and stick in the run queue */
|
||||
enter_critical_section();
|
||||
t = list_remove_head_type(&q->task_list, struct task, queue_node);
|
||||
|
||||
if (t) {
|
||||
ASSERT(t->state == TASK_BLOCKED);
|
||||
t->state = TASK_READY;
|
||||
insert_run_q_tail(t);
|
||||
ret = 1;
|
||||
}
|
||||
exit_critical_section();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* profile the task manager */
|
||||
void task_get_statistics(struct idle_statistics *s)
|
||||
{
|
||||
u_int64_t now;
|
||||
|
||||
enter_critical_section();
|
||||
now = timer_get_ticks();
|
||||
s->uptime_ticks = now - boot_time_ticks;
|
||||
s->ticksHz = timer_get_tick_rate();
|
||||
s->idle_ticks = idle_ticks;
|
||||
s->idles = shallow_idle_cnt;
|
||||
s->deep_idle_ticks = deep_idle_ticks;
|
||||
s->deep_idles = deep_idle_cnt;
|
||||
s->threshold_us = deep_idle_threshold_us;
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
|
||||
void task_set_idle_threshold(u_int32_t us)
|
||||
{
|
||||
deep_idle_threshold_us = us;
|
||||
}
|
||||
|
||||
/* event stuff */
|
||||
|
||||
void event_init(struct task_event *event, uint32_t flags, bool initial_state)
|
||||
{
|
||||
ASSERT(NULL != event);
|
||||
|
||||
wait_queue_init(&event->wait);
|
||||
event->signalled = initial_state;
|
||||
event->flags = flags;
|
||||
}
|
||||
|
||||
void event_signal(struct task_event *event)
|
||||
{
|
||||
enter_critical_section();
|
||||
event->signalled = true;
|
||||
|
||||
if (event->flags & EVENT_FLAG_AUTO_UNSIGNAL) {
|
||||
/* only allow one task to get through and unsignal */
|
||||
if (wait_queue_wake_one(&event->wait) > 0) {
|
||||
event->signalled = false;
|
||||
}
|
||||
} else {
|
||||
wait_queue_wake_all(&event->wait);
|
||||
}
|
||||
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
void event_unsignal(struct task_event *event)
|
||||
{
|
||||
event->signalled = false;
|
||||
}
|
||||
|
||||
void event_wait(struct task_event *event)
|
||||
{
|
||||
enter_critical_section();
|
||||
if (event->signalled == false) {
|
||||
/* block on the wait queue */
|
||||
task_block(&event->wait);
|
||||
} else {
|
||||
/* we're the first task here, unsignal the lock */
|
||||
if (event->flags & EVENT_FLAG_AUTO_UNSIGNAL)
|
||||
event->signalled = false;
|
||||
}
|
||||
exit_critical_section();
|
||||
}
|
||||
|
||||
/* happens at interrupt context */
|
||||
struct event_wait_callback_info {
|
||||
struct task_event *event;
|
||||
struct task *task;
|
||||
bool timed_out;
|
||||
};
|
||||
|
||||
static void event_wait_callback(struct callout *c, void *_info)
|
||||
{
|
||||
struct event_wait_callback_info *info = (struct event_wait_callback_info *)_info;
|
||||
struct task_wait_queue *q = &info->event->wait;
|
||||
struct task *task = info->task;
|
||||
struct task *t, *temp;
|
||||
|
||||
/* we may be racing with a legitimate wakeup, in which case we have not timed out */
|
||||
if (TASK_BLOCKED != task->state)
|
||||
return;
|
||||
|
||||
/* verify that we are blocked on the queue we think we're blocked on */
|
||||
list_for_every_entry_safe(&q->task_list, t, temp, struct task, queue_node) {
|
||||
if (t == task) {
|
||||
list_delete(&t->queue_node);
|
||||
|
||||
ASSERT(t->state == TASK_BLOCKED);
|
||||
t->state = TASK_READY;
|
||||
insert_run_q_tail(t);
|
||||
|
||||
info->timed_out = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool event_wait_timeout(struct task_event *event, utime_t delay)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
enter_critical_section();
|
||||
ret = event->signalled;
|
||||
if (false == ret) {
|
||||
|
||||
/* are we allowed to wait? */
|
||||
if (delay > 0) {
|
||||
struct event_wait_callback_info info;
|
||||
info.event = event;
|
||||
info.task = current_task;
|
||||
info.timed_out = false;
|
||||
|
||||
/* block on the wait queue */
|
||||
callout_enqueue(¤t_task->sleep_callout, delay, event_wait_callback, &info);
|
||||
task_block(&event->wait);
|
||||
callout_dequeue(¤t_task->sleep_callout);
|
||||
|
||||
/* if we didn't time out, the event was signalled and we woke because of it */
|
||||
if (false == info.timed_out)
|
||||
ret = true;
|
||||
}
|
||||
} else {
|
||||
/* we're the first task here, unsignal the lock */
|
||||
if (event->flags & EVENT_FLAG_AUTO_UNSIGNAL)
|
||||
event->signalled = false;
|
||||
}
|
||||
|
||||
exit_critical_section();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* debug stuff */
|
||||
static const char *task_state_to_string(enum task_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case TASK_INITIAL:
|
||||
return "initial";
|
||||
case TASK_READY:
|
||||
return "ready";
|
||||
case TASK_RUNNING:
|
||||
return "running";
|
||||
case TASK_BLOCKED:
|
||||
return "blocked";
|
||||
case TASK_SLEEPING:
|
||||
return "sleeping";
|
||||
case TASK_FINISHED:
|
||||
return "finished";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_task(struct task *t)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "task %p (%s):\n", t, t->name);
|
||||
dprintf(DEBUG_CRITICAL, " state %d (%s) interrupts %sabled (%d)\n",
|
||||
t->state,
|
||||
task_state_to_string(t->state),
|
||||
(t->irq_disable_count == 0) ? "en" : "dis",
|
||||
t->irq_disable_count);
|
||||
dprintf(DEBUG_CRITICAL, " stack base %p, len 0x%zx, ", t->stack_base, t->stack_len);
|
||||
|
||||
if (task_stack_magic != *(uint32_t *)t->stack_base) {
|
||||
dprintf(DEBUG_CRITICAL, "overflowed\n");
|
||||
} else {
|
||||
for (i = 0; i < (t->stack_len / sizeof(uint32_t)); i++)
|
||||
if (task_stack_magic != *((uint32_t *)t->stack_base + i))
|
||||
break;
|
||||
dprintf(DEBUG_CRITICAL, "used 0x%lx\n", t->stack_len - (i * sizeof(uint32_t)));
|
||||
}
|
||||
printf(" start routine %p, args %p\n", t->routine, t->arg);
|
||||
}
|
||||
|
||||
static int do_task(int argc, struct cmd_arg *args)
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("not enough arguments\n");
|
||||
usage:
|
||||
printf("usage: \n");
|
||||
printf("%s list\n", args[0].str);
|
||||
printf("%s uptime\n", args[0].str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(args[1].str, "list")) {
|
||||
struct task *t;
|
||||
|
||||
list_for_every_entry(&task_list, t, struct task, task_list_node) {
|
||||
dump_task(t);
|
||||
}
|
||||
} else if (!strcmp(args[1].str, "uptime")) {
|
||||
uint64_t now;
|
||||
utime_t elapsed, idle, active;
|
||||
|
||||
now = timer_get_ticks();
|
||||
|
||||
/* convert to microseconds */
|
||||
elapsed = timer_ticks_to_usecs(now - boot_time_ticks);
|
||||
idle = timer_ticks_to_usecs(idle_ticks);
|
||||
active = elapsed - idle;
|
||||
printf("up %u.%06u idle %u.%06u active %u.%06u\n",
|
||||
(unsigned)(elapsed / (1000 * 1000)), (unsigned)(elapsed % (1000 * 1000)),
|
||||
(unsigned)(idle / (1000 * 1000)), (unsigned)(idle % (1000 * 1000)),
|
||||
(unsigned)(active / (1000 * 1000)), (unsigned)(active % (1000 * 1000)));
|
||||
} else {
|
||||
printf("unrecognized subcommand\n");
|
||||
goto usage;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MENU_COMMAND_DEVELOPMENT(task, do_task, "examine system tasks", NULL);
|
||||
|
||||
/*
|
||||
* Validate that a task pointer points to something that looks like a task.
|
||||
*/
|
||||
static bool task_ptr_sanity(struct task *t)
|
||||
{
|
||||
if ((u_int32_t)t % 4) {
|
||||
dprintf(DEBUG_CRITICAL, "task pointer %p bad, task list corrupt\n", t);
|
||||
return(false);
|
||||
}
|
||||
/* add extra bad-address checks here */
|
||||
|
||||
if ((TASK_MAGIC != t->magic) || (TASK_MAGIC2 != t->magic2)) {
|
||||
dprintf(DEBUG_CRITICAL, "task %p (corrupt)\n", t);
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
static void do_task_panic(void *junk)
|
||||
{
|
||||
struct task *t;
|
||||
int tcount;
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "current ");
|
||||
if (task_ptr_sanity(current_task)) {
|
||||
dump_task(current_task);
|
||||
arch_backtrace_current_task(current_task->stack_base, current_task->stack_len);
|
||||
}
|
||||
tcount = 0;
|
||||
list_for_every_entry(&task_list, t, struct task, task_list_node) {
|
||||
if (current_task != t) {
|
||||
if (!task_ptr_sanity(t))
|
||||
break;
|
||||
|
||||
/* paranoia */
|
||||
t->name[TASK_NAME_MAX] = 0;
|
||||
dump_task(t);
|
||||
arch_backtrace_task(&t->arch, t->stack_base, t->stack_len);
|
||||
}
|
||||
/* XXX tasks aren't properly reaped so many tasks exiting will confuse this */
|
||||
if (tcount++ > (task_count + 2)) {
|
||||
dprintf(DEBUG_CRITICAL, "too many tasks, task list probably corrupt\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PANIC_HOOK(task, do_task_panic, NULL);
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <unittest.h>
|
||||
#include <list.h>
|
||||
|
||||
void test_list_is_empty(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
struct list_node list2;
|
||||
|
||||
struct list_node item;
|
||||
struct list_node item2;
|
||||
|
||||
TEST_ASSERT(list_is_empty(&list));
|
||||
|
||||
list_add_head(&list, &item);
|
||||
|
||||
TEST_ASSERT(!list_is_empty(&list));
|
||||
|
||||
list_initialize(&list2);
|
||||
|
||||
TEST_ASSERT(list_is_empty(&list2));
|
||||
|
||||
list_add_head(&list2, &item2);
|
||||
|
||||
TEST_ASSERT(!list_is_empty(&list2));
|
||||
}
|
||||
|
||||
void test_list_add_head(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
struct list_node *iter;
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
struct list_node item3;
|
||||
|
||||
struct list_node* items[] = {&item1, &item2, &item3};
|
||||
int i;
|
||||
|
||||
list_add_head(&list, &item1);
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 1);
|
||||
TEST_ASSERT_PTR_EQ(items[1 - (i + 1)], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
list_add_head(&list, &item2);
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 2);
|
||||
TEST_ASSERT_PTR_EQ(items[2 - (i + 1)], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
list_add_head(&list, &item3);
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 3);
|
||||
TEST_ASSERT_PTR_EQ(items[3 - (i + 1)], iter);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void test_list_add_tail(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
struct list_node *iter;
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
struct list_node item3;
|
||||
|
||||
struct list_node* items[] = {&item1, &item2, &item3};
|
||||
int i;
|
||||
|
||||
list_add_tail(&list, &item1);
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 1);
|
||||
TEST_ASSERT_PTR_EQ(items[i], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
list_add_tail(&list, &item2);
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 2);
|
||||
TEST_ASSERT_PTR_EQ(items[i], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
list_add_tail(&list, &item3);
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 3);
|
||||
TEST_ASSERT_PTR_EQ(items[i], iter);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void test_list_remove_head(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
struct list_node *iter;
|
||||
struct list_node *removed;
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
struct list_node item3;
|
||||
|
||||
struct list_node* items[] = {&item1, &item2, &item3};
|
||||
int i;
|
||||
|
||||
list_add_tail(&list, &item1);
|
||||
list_add_tail(&list, &item2);
|
||||
list_add_tail(&list, &item3);
|
||||
|
||||
removed = list_remove_head(&list);
|
||||
TEST_ASSERT_PTR_EQ(removed, &item1);
|
||||
TEST_ASSERT(!list_in_list(removed));
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 2);
|
||||
TEST_ASSERT_PTR_EQ(items[i + 1], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
removed = list_remove_head(&list);
|
||||
TEST_ASSERT_PTR_EQ(removed, &item2);
|
||||
TEST_ASSERT(!list_in_list(removed));
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 1);
|
||||
TEST_ASSERT_PTR_EQ(items[i + 2], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
removed = list_remove_head(&list);
|
||||
TEST_ASSERT_PTR_EQ(removed, &item3);
|
||||
TEST_ASSERT(!list_in_list(removed));
|
||||
TEST_ASSERT(list_is_empty(&list));
|
||||
|
||||
removed = list_remove_head(&list);
|
||||
TEST_ASSERT_NULL(removed);
|
||||
TEST_ASSERT(list_is_empty(&list));
|
||||
}
|
||||
|
||||
void test_list_remove_tail(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
struct list_node *iter;
|
||||
struct list_node *removed;
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
struct list_node item3;
|
||||
|
||||
struct list_node* items[] = {&item1, &item2, &item3};
|
||||
int i;
|
||||
|
||||
list_add_tail(&list, &item1);
|
||||
list_add_tail(&list, &item2);
|
||||
list_add_tail(&list, &item3);
|
||||
|
||||
removed = list_remove_tail(&list);
|
||||
TEST_ASSERT_PTR_EQ(removed, &item3);
|
||||
TEST_ASSERT(!list_in_list(removed));
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 2);
|
||||
TEST_ASSERT_PTR_EQ(items[i], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
removed = list_remove_tail(&list);
|
||||
TEST_ASSERT_PTR_EQ(removed, &item2);
|
||||
TEST_ASSERT(!list_in_list(removed));
|
||||
|
||||
i = 0;
|
||||
list_for_every(&list, iter) {
|
||||
TEST_ASSERT_LT(i, 1);
|
||||
TEST_ASSERT_PTR_EQ(items[i], iter);
|
||||
i++;
|
||||
}
|
||||
|
||||
removed = list_remove_tail(&list);
|
||||
TEST_ASSERT_PTR_EQ(removed, &item1);
|
||||
TEST_ASSERT(!list_in_list(removed));
|
||||
TEST_ASSERT(list_is_empty(&list));
|
||||
|
||||
removed = list_remove_head(&list);
|
||||
TEST_ASSERT_NULL(removed);
|
||||
TEST_ASSERT(list_is_empty(&list));
|
||||
}
|
||||
|
||||
void test_safe_unlink_double_free(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
|
||||
list_add_tail(&list, &item1);
|
||||
list_add_tail(&list, &item2);
|
||||
|
||||
list_delete(&item1);
|
||||
|
||||
TEST_EXPECT_PANIC();
|
||||
list_delete(&item1);
|
||||
TEST_EXPECT_PANICKED();
|
||||
}
|
||||
|
||||
void test_safe_unlink_next_corrupt(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
|
||||
list_add_tail(&list, &item1);
|
||||
list_add_tail(&list, &item2);
|
||||
|
||||
item1.next = item1.next->next;
|
||||
|
||||
TEST_EXPECT_PANIC();
|
||||
list_delete(&item1);
|
||||
TEST_EXPECT_PANICKED();
|
||||
}
|
||||
|
||||
void test_safe_unlink_prev_corrupt(uintptr_t param)
|
||||
{
|
||||
struct list_node list = LIST_INITIAL_VALUE(list);
|
||||
|
||||
struct list_node item1;
|
||||
struct list_node item2;
|
||||
|
||||
list_add_tail(&list, &item1);
|
||||
list_add_tail(&list, &item2);
|
||||
|
||||
item1.prev = item1.prev->prev;
|
||||
|
||||
TEST_EXPECT_PANIC();
|
||||
list_delete(&item1);
|
||||
TEST_EXPECT_PANICKED();
|
||||
}
|
||||
|
||||
struct test_suite linked_list_suite = {
|
||||
.name = "linked_list",
|
||||
.test_cases = {
|
||||
{ "list_is_empty", test_list_is_empty, 0, "Tests list_is_empty" },
|
||||
{ "list_add_head", test_list_add_head, 0, "Tests list_add_head" },
|
||||
{ "list_add_tail", test_list_add_tail, 0, "Tests list_add_tail" },
|
||||
{ "list_remove_head", test_list_remove_head, 0, "Tests list_remove_head" },
|
||||
{ "list_remove_tail", test_list_remove_tail, 0, "Tests list_remove_tail" },
|
||||
{ "safe_unlink_double_free", test_safe_unlink_double_free, 0, "Tests removing an item twice is detected" },
|
||||
{ "safe_unlink_next_corrupt", test_safe_unlink_next_corrupt, 0, "Tests corrupting the next pointer is detected" },
|
||||
{ "safe_unlink_prev_corrupt", test_safe_unlink_prev_corrupt, 0, "Tests corrupting the next pointer is detected" },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(linked_list_suite);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2014-2015 Apple Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
|
||||
#include <unittest.h>
|
||||
#include <corecrypto/ccsha2.h>
|
||||
#include <string.h>
|
||||
|
||||
void sha384_calculate(const void *in_ptr, size_t in_len, void *out_ptr, size_t out_len);
|
||||
|
||||
struct sha384_test {
|
||||
const char *data;
|
||||
uint32_t len;
|
||||
uint8_t expected[CCSHA384_OUTPUT_SIZE];
|
||||
};
|
||||
|
||||
static struct sha384_test sha384_tests[] = {
|
||||
{ "", 0,
|
||||
{ 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b} },
|
||||
|
||||
{ "abc", 3,
|
||||
{ 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 } },
|
||||
|
||||
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56,
|
||||
{ 0x33, 0x91, 0xfd, 0xdd, 0xfc, 0x8d, 0xc7, 0x39, 0x37, 0x07, 0xa6, 0x5b, 0x1b, 0x47, 0x09, 0x39, 0x7c, 0xf8, 0xb1, 0xd1, 0x62, 0xaf, 0x05, 0xab, 0xfe, 0x8f, 0x45, 0x0d, 0xe5, 0xf3, 0x6b, 0xc6, 0xb0, 0x45, 0x5a, 0x85, 0x20, 0xbc, 0x4e, 0x6f, 0x5f, 0xe9, 0x5b, 0x1f, 0xe3, 0xc8, 0x45, 0x2b } },
|
||||
|
||||
};
|
||||
|
||||
void test_sha384(uintptr_t param)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof sha384_tests / sizeof(sha384_tests[0]); i++) {
|
||||
struct sha384_test *test = &sha384_tests[i];
|
||||
|
||||
uint8_t calculated[CCSHA384_OUTPUT_SIZE];
|
||||
|
||||
sha384_calculate((void *)test->data, test->len, (void *)calculated, sizeof(calculated));
|
||||
|
||||
TEST_ASSERT_MEM_EQ(calculated, test->expected, sizeof(test->expected));
|
||||
}
|
||||
}
|
||||
|
||||
struct test_suite sha384_suite = {
|
||||
.name = "sha384",
|
||||
.test_cases = {
|
||||
{ "sha384", test_sha384, 0, "Tests the sha384_calculate function" },
|
||||
TEST_CASE_LAST
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE(sha384_suite);
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2014 Apple Inc. All rights reserved.
|
||||
#
|
||||
# This document is the property of Apple Inc.
|
||||
# It is considered confidential and proprietary.
|
||||
#
|
||||
# This document may not be reproduced or transmitted in any form,
|
||||
# in whole or in part, without the express written permission of
|
||||
# Apple Inc.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
TEST_NAME := iboot-sys
|
||||
|
||||
TEST_OBJS := \
|
||||
$(LOCAL_DIR)/hash.o
|
||||
|
||||
TEST_SUPPORT_OBJS := \
|
||||
tests/unittest-main.o \
|
||||
$(LOCAL_DIR)/tests-sha384.o \
|
||||
$(LOCAL_DIR)/tests-list.o
|
||||
|
||||
HOST_SDKROOT_PATH := $(shell xcodebuild -version -sdk macosx.internal Path)
|
||||
|
||||
TEST_CFLAGS := \
|
||||
-DWITH_SHA2_384=1 \
|
||||
-I$(HOST_SDKROOT_PATH)/usr/local/include
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* This document is the property of Apple Inc.
|
||||
* It is considered confidential and proprietary.
|
||||
*
|
||||
* This document may not be reproduced or transmitted in any form,
|
||||
* in whole or in part, without the express written permission of
|
||||
* Apple Inc.
|
||||
*/
|
||||
#include <lib/libc.h> /* for __unused */
|
||||
#include <sys.h>
|
||||
#include <platform/timer.h>
|
||||
#include <drivers/power.h>
|
||||
|
||||
#if WITH_HW_TIMER
|
||||
utime_t system_time(void)
|
||||
{
|
||||
return timer_ticks_to_usecs(timer_get_ticks());
|
||||
}
|
||||
|
||||
bool time_has_elapsed(utime_t start_time, utime_t timeout)
|
||||
{
|
||||
return ((system_time() - start_time) >= timeout) ? true : false;
|
||||
}
|
||||
|
||||
void spin(utime_t usecs)
|
||||
{
|
||||
utime_t t;
|
||||
|
||||
t = system_time();
|
||||
while (system_time() - t < usecs)
|
||||
;
|
||||
}
|
||||
#else
|
||||
utime_t system_time(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool time_has_elapsed(utime_t start_time __unused, utime_t timeout __unused)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void spin(utime_t usecs __unused)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if WITH_HW_POWER
|
||||
|
||||
utime_t calendar_time(void)
|
||||
{
|
||||
return power_get_calendar_time();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
utime_t calendar_time(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user