first and last commit
This commit is contained in:
commit
1bf4efd552
2612 files changed
+636500
No files matched your search
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <drivers/ipipe.h>
|
||||
#include <lib/cbuf.h>
|
||||
#include <platform.h>
|
||||
#include <platform/memmap.h>
|
||||
#include <sys/menu.h>
|
||||
#include <sys/task.h>
|
||||
#include <stdint.h>
|
||||
#include "ipipe_protocol.h"
|
||||
|
||||
#define IPIPE_FILE_RX_INDEX (1)
|
||||
#define IPIPE_CONSOLE_INDEX (2)
|
||||
#define IPIPE_FILE_TX_INDEX (3)
|
||||
|
||||
#define IPIPE_HEADER_LEN (4)
|
||||
|
||||
#define IDBG_PROTO_ERR (DEBUG_CRITICAL)
|
||||
|
||||
struct ipipe {
|
||||
uint32_t max_packet_size;
|
||||
ipipe_tx_callback_t *tx_callback;
|
||||
void *priv;
|
||||
|
||||
uint8_t *tx_buffer;
|
||||
|
||||
bool shutdown;
|
||||
|
||||
bool enabled;
|
||||
|
||||
// Console stuff
|
||||
struct cbuf *console_output_cbuf;
|
||||
struct task_event console_out_event;
|
||||
struct task_event console_out_done_event;
|
||||
struct task *console_task;
|
||||
|
||||
// File get (including "DFU" mode) stuff
|
||||
bool file_rx_active;
|
||||
uint32_t file_rx_len;
|
||||
uint8_t *file_rx_buffer;
|
||||
uint32_t file_rx_buffer_len;
|
||||
struct task_event file_rx_event;
|
||||
#if !WITH_TBT_MODE_DFU
|
||||
struct callout file_rx_callout;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int ipipe_console_task(void *arg);
|
||||
static void ipipe_handle_console_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes);
|
||||
|
||||
static void ipipe_handle_file_rx_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes);
|
||||
static void ipipe_send_file_rx_ack(ipipe_t *ipipe, uint32_t offset);
|
||||
static void ipipe_send_file_rx_error(ipipe_t *ipipe, uint32_t offset, uint32_t error);
|
||||
static void ipipe_file_rx_failed(ipipe_t *ipipe);
|
||||
static void ipipe_handle_file_rx_callout(struct callout *co, void *arg);
|
||||
|
||||
static void ipipe_handle_file_tx_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes);
|
||||
|
||||
ipipe_t *g_ipipe;
|
||||
|
||||
ipipe_t *ipipe_init(uint32_t max_packet_size, ipipe_tx_callback_t *tx_callback, void *priv)
|
||||
{
|
||||
ipipe_t *ipipe;
|
||||
|
||||
ASSERT(max_packet_size > 64);
|
||||
|
||||
ipipe = calloc(sizeof(*ipipe), 1);
|
||||
ipipe->max_packet_size = max_packet_size;
|
||||
ipipe->tx_callback = tx_callback;
|
||||
ipipe->priv = priv;
|
||||
|
||||
ipipe->tx_buffer = malloc(max_packet_size);
|
||||
|
||||
#if WITH_TBT_MODE_RECOVERY
|
||||
ipipe->console_output_cbuf = cbuf_create(4096, NULL);
|
||||
event_init(&ipipe->console_out_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
event_init(&ipipe->console_out_done_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
ipipe->console_task = task_create("ipipe_console", ipipe_console_task, ipipe, 0);
|
||||
task_start(ipipe->console_task);
|
||||
#endif
|
||||
|
||||
event_init(&ipipe->file_rx_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
g_ipipe = ipipe;
|
||||
|
||||
return ipipe;
|
||||
}
|
||||
|
||||
void ipipe_quiesce_and_free(ipipe_t *ipipe)
|
||||
{
|
||||
if (g_ipipe == ipipe)
|
||||
g_ipipe = NULL;
|
||||
|
||||
#if WITH_TBT_MODE_RECOVERY
|
||||
ipipe->shutdown = true;
|
||||
event_signal(&ipipe->console_out_event);
|
||||
task_wait_on(ipipe->console_task);
|
||||
task_destroy(ipipe->console_task);
|
||||
#endif
|
||||
|
||||
free(ipipe->tx_buffer);
|
||||
free(ipipe);
|
||||
}
|
||||
|
||||
static void ipipe_packet_set_header(uint8_t *packet, uint16_t pipe_idx, uint16_t bytes)
|
||||
{
|
||||
packet[0] = pipe_idx & 0xff;
|
||||
packet[1] = (pipe_idx >> 8) & 0xff;
|
||||
packet[2] = (bytes & 0xff);
|
||||
packet[3] = (bytes >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static void ipipe_send(ipipe_t *ipipe, uint8_t *packet, uint32_t bytes)
|
||||
{
|
||||
ipipe->tx_callback(ipipe->priv, packet, bytes);
|
||||
}
|
||||
|
||||
// Process a data packet. Packets are expected to have the following format.
|
||||
// Invalid packets are simply dropped.
|
||||
// Bytes 0-1: Pipe number
|
||||
// Bytes 2-3: Payload size (bytes)
|
||||
void ipipe_handle_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes)
|
||||
{
|
||||
uint16_t pipe_idx;
|
||||
uint16_t ipipe_bytes;
|
||||
const uint8_t *payload;
|
||||
|
||||
// Packet needs to be big enough to hold the iPipe header
|
||||
if (bytes < IPIPE_HEADER_LEN) {
|
||||
dprintf(IDBG_PROTO_ERR, "packet length %u too short for header\n", bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
pipe_idx = packet[0] | (packet[1] << 8);
|
||||
ipipe_bytes = packet[2] | (packet[3] << 8);
|
||||
|
||||
// And packet needs to be big enough for header and the payload size listed in the header
|
||||
if (ipipe_bytes + IPIPE_HEADER_LEN > bytes) {
|
||||
dprintf(IDBG_PROTO_ERR, "packet length %u too short for payload %u\n", bytes, ipipe_bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
payload = packet + IPIPE_HEADER_LEN;
|
||||
|
||||
if (pipe_idx == IPIPE_CONSOLE_INDEX) {
|
||||
ipipe_handle_console_packet(ipipe, payload, ipipe_bytes);
|
||||
|
||||
} else if (pipe_idx == IPIPE_FILE_RX_INDEX) {
|
||||
ipipe_handle_file_rx_packet(ipipe, payload, ipipe_bytes);
|
||||
|
||||
} else if (pipe_idx == IPIPE_FILE_TX_INDEX) {
|
||||
ipipe_handle_file_tx_packet(ipipe, payload, ipipe_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// iPipe file RX
|
||||
//
|
||||
|
||||
void ipipe_start_file_rx(ipipe_t *ipipe, void *load_address, uint32_t length)
|
||||
{
|
||||
if (!security_allow_memory(load_address, length)) {
|
||||
if (ipipe->file_rx_active) {
|
||||
ipipe->file_rx_active = false;
|
||||
event_signal(&ipipe->file_rx_event);
|
||||
}
|
||||
ipipe->file_rx_len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ipipe->file_rx_buffer = load_address;
|
||||
ipipe->file_rx_buffer_len = length;
|
||||
ipipe->file_rx_len = 0;
|
||||
ipipe->file_rx_active = true;
|
||||
}
|
||||
|
||||
int thunderboot_get_dfu_image(void *load_address, int load_length)
|
||||
{
|
||||
int status = -1;
|
||||
|
||||
ASSERT(load_length > 0);
|
||||
ASSERT(load_address != NULL);
|
||||
|
||||
ipipe_t *ipipe = g_ipipe;
|
||||
if (ipipe == NULL)
|
||||
goto exit;
|
||||
|
||||
ipipe_start_file_rx(ipipe, load_address, load_length);
|
||||
if (!ipipe->file_rx_active)
|
||||
return -1;
|
||||
|
||||
event_wait(&ipipe->file_rx_event);
|
||||
|
||||
if (ipipe->file_rx_len == 0)
|
||||
status = -1;
|
||||
else
|
||||
status = ipipe->file_rx_len;
|
||||
|
||||
exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void ipipe_handle_file_rx_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes)
|
||||
{
|
||||
uint32_t offset;
|
||||
uint32_t payload_size;
|
||||
|
||||
// packet needs to be big enough for the header
|
||||
if (bytes < 4) {
|
||||
ipipe_send_file_rx_error(ipipe, 0, IPIPE_ERR_FRAMING);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = packet[0] | (packet[1] << 8) | (packet[2] << 16) | (packet[3] << 24);
|
||||
payload_size = bytes - 4;
|
||||
|
||||
// in non-DFU mode, hosts can start a transfer with a reset packet (offset 0 and no payload)
|
||||
// in order to reset back to the default load address.
|
||||
// in DFU-mode, this is just a no-op
|
||||
if (offset == 0 && payload_size == 0) {
|
||||
#if !WITH_TBT_MODE_DFU
|
||||
ipipe_start_file_rx(ipipe, (void *)DEFAULT_LOAD_ADDRESS, DEFAULT_RAMDISK_SIZE);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ipipe->file_rx_active) {
|
||||
ipipe_send_file_rx_error(ipipe, offset, IPIPE_ERR_TBD);
|
||||
return;
|
||||
}
|
||||
|
||||
// a payload size of 0 with a non-zero offset means that the transfer is done
|
||||
if (payload_size == 0) {
|
||||
ipipe->file_rx_active = false;
|
||||
if (offset != ipipe->file_rx_len) {
|
||||
ipipe_send_file_rx_error(ipipe, offset, IPIPE_ERR_TBD);
|
||||
ipipe->file_rx_len = 0;
|
||||
} else {
|
||||
ipipe_send_file_rx_ack(ipipe, ipipe->file_rx_len);
|
||||
}
|
||||
event_signal(&ipipe->file_rx_event);
|
||||
|
||||
#if !WITH_TBT_MODE_DFU
|
||||
callout_dequeue(&ipipe->file_rx_callout);
|
||||
#endif
|
||||
} else {
|
||||
if (offset > ipipe->file_rx_buffer_len ||
|
||||
offset + payload_size < offset ||
|
||||
offset + payload_size > ipipe->file_rx_buffer_len) {
|
||||
ipipe_send_file_rx_error(ipipe, offset, IPIPE_ERR_TBD);
|
||||
ipipe_file_rx_failed(ipipe);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!security_allow_memory((uint8_t *)ipipe->file_rx_buffer + offset, payload_size)) {
|
||||
ipipe_send_file_rx_error(ipipe, offset, IPIPE_ERR_TBD);
|
||||
ipipe_file_rx_failed(ipipe);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy((uint8_t *)ipipe->file_rx_buffer + offset, packet + 4, payload_size);
|
||||
|
||||
if (offset + payload_size > ipipe->file_rx_len)
|
||||
ipipe->file_rx_len = offset + payload_size;
|
||||
|
||||
#if !WITH_TBT_MODE_DFU
|
||||
callout_reset(&ipipe->file_rx_callout, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
ipipe_send_file_rx_ack(ipipe, offset);
|
||||
}
|
||||
|
||||
static void ipipe_file_rx_failed(ipipe_t *ipipe)
|
||||
{
|
||||
ipipe->file_rx_active = false;
|
||||
ipipe_send_file_rx_error(ipipe, -1, IPIPE_ERR_TBD);
|
||||
ipipe->file_rx_len = 0;
|
||||
|
||||
dprintf(DEBUG_CRITICAL, "transfer failed\n");
|
||||
|
||||
event_signal(&ipipe->file_rx_event);
|
||||
#if !WITH_TBT_MODE_DFU
|
||||
callout_dequeue(&ipipe->file_rx_callout);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ipipe_send_file_rx_ack(ipipe_t *ipipe, uint32_t offset)
|
||||
{
|
||||
uint8_t *packet = ipipe->tx_buffer;
|
||||
|
||||
ipipe_packet_set_header(packet, IPIPE_FILE_RX_INDEX, 8);
|
||||
|
||||
packet[IPIPE_HEADER_LEN + 0] = offset & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 1] = (offset >> 8) & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 2] = (offset >> 16) & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 3] = (offset >> 24) & 0xff;
|
||||
|
||||
packet[IPIPE_HEADER_LEN + 4] = 0;
|
||||
packet[IPIPE_HEADER_LEN + 5] = 0;
|
||||
packet[IPIPE_HEADER_LEN + 6] = 0;
|
||||
packet[IPIPE_HEADER_LEN + 7] = 0;
|
||||
|
||||
ipipe_send(ipipe, packet, IPIPE_HEADER_LEN + 8);
|
||||
}
|
||||
|
||||
static void ipipe_send_file_rx_error(ipipe_t *ipipe, uint32_t offset, uint32_t error)
|
||||
{
|
||||
uint8_t *packet = ipipe->tx_buffer;
|
||||
|
||||
ipipe_packet_set_header(packet, IPIPE_FILE_RX_INDEX, 8);
|
||||
|
||||
packet[IPIPE_HEADER_LEN + 0] = offset & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 1] = (offset >> 8) & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 2] = (offset >> 16) & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 3] = (offset >> 24) & 0xff;
|
||||
|
||||
packet[IPIPE_HEADER_LEN + 4] = error & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 5] = (error >> 8) & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 6] = (error >> 16) & 0xff;
|
||||
packet[IPIPE_HEADER_LEN + 7] = (error >> 24) & 0xff;
|
||||
|
||||
ipipe_send(ipipe, packet, IPIPE_HEADER_LEN + 8);
|
||||
}
|
||||
|
||||
//
|
||||
// iPipe file TX
|
||||
//
|
||||
|
||||
static void ipipe_handle_file_tx_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes)
|
||||
{
|
||||
// XXX: todo
|
||||
}
|
||||
|
||||
//
|
||||
// iPipe console
|
||||
//
|
||||
|
||||
static void ipipe_handle_console_packet(ipipe_t *ipipe, const uint8_t *packet, uint32_t bytes)
|
||||
{
|
||||
for(unsigned i = 0; i < bytes; i++)
|
||||
debug_pushchar(packet[i]);
|
||||
}
|
||||
|
||||
static int32_t ipipe_console_flush_write_cbuf(ipipe_t *ipipe)
|
||||
{
|
||||
uint8_t *packet = ipipe->tx_buffer;
|
||||
uint32_t bytes = 0;
|
||||
|
||||
do {
|
||||
char c;
|
||||
if (cbuf_read_char(ipipe->console_output_cbuf, &c) == 0)
|
||||
break;
|
||||
packet[IPIPE_HEADER_LEN + bytes] = c;
|
||||
|
||||
bytes++;
|
||||
} while (bytes + IPIPE_HEADER_LEN < ipipe->max_packet_size);
|
||||
|
||||
if (bytes == 0)
|
||||
return 0;
|
||||
|
||||
ipipe_packet_set_header(packet, IPIPE_CONSOLE_INDEX, bytes);
|
||||
|
||||
ipipe_send(ipipe, packet, bytes + IPIPE_HEADER_LEN);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int ipipe_console_task(void *arg)
|
||||
{
|
||||
ipipe_t *ipipe = (ipipe_t *)arg;
|
||||
|
||||
while(!ipipe->shutdown) {
|
||||
if (ipipe_console_flush_write_cbuf(ipipe) == 0)
|
||||
event_wait(&ipipe->console_out_event);
|
||||
else
|
||||
event_signal(&ipipe->console_out_done_event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void thunderboot_putchar(int c)
|
||||
{
|
||||
ipipe_t *ipipe = g_ipipe;
|
||||
|
||||
if (ipipe == NULL)
|
||||
return;
|
||||
|
||||
while (cbuf_write_char(ipipe->console_output_cbuf, c) == 0)
|
||||
event_wait(&ipipe->console_out_done_event);
|
||||
|
||||
event_signal(&ipipe->console_out_event);
|
||||
}
|
||||
|
||||
int thunderboot_serial_send_cmd_string(uint8_t *buffer, uint32_t len)
|
||||
{
|
||||
// XXX: this isn't robust to losing the connection mid-command
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
thunderboot_putchar(buffer[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_ipipe_puts(int argc, struct cmd_arg *argv)
|
||||
{
|
||||
if (g_ipipe == NULL) {
|
||||
printf("iPipe not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (i > 1)
|
||||
thunderboot_putchar(' ');
|
||||
for (int j = 0; argv[i].str[j] != 0; j++)
|
||||
thunderboot_putchar(argv[i].str[j]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void thunderboot_transfer_prepare(void *load_address, uint32_t length)
|
||||
{
|
||||
ipipe_t *ipipe = g_ipipe;
|
||||
|
||||
ipipe_start_file_rx(ipipe, load_address, length);
|
||||
|
||||
#if !WITH_TBT_MODE_DFU
|
||||
// callout to abort the transfer if more than a second elapses without a packet
|
||||
callout_enqueue(&ipipe->file_rx_callout, 10000000, ipipe_handle_file_rx_callout, ipipe);
|
||||
#endif
|
||||
}
|
||||
|
||||
# if !WITH_TBT_MODE_DFU
|
||||
static void ipipe_handle_file_rx_callout(struct callout *co, void *arg)
|
||||
{
|
||||
ipipe_t *ipipe = (ipipe_t *)arg;
|
||||
|
||||
printf("TBT timeout\n");
|
||||
ipipe_file_rx_failed(ipipe);
|
||||
}
|
||||
#endif
|
||||
|
||||
int thunderboot_transfer_wait(void)
|
||||
{
|
||||
ipipe_t *ipipe = g_ipipe;
|
||||
|
||||
while (ipipe->file_rx_active)
|
||||
event_wait(&ipipe->file_rx_event);
|
||||
|
||||
if (ipipe->file_rx_len == 0)
|
||||
return -1;
|
||||
else
|
||||
return ipipe->file_rx_len;
|
||||
|
||||
}
|
||||
|
||||
MENU_COMMAND_DEBUG(ipipe_puts, do_ipipe_puts, "ipipe puts", NULL);
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef IPIPE_PROTOCOL_H
|
||||
#define IPIPE_PROTOCOL_H
|
||||
|
||||
#if 0
|
||||
#define IPIPE_CTL_PIPE (0)
|
||||
#endif
|
||||
|
||||
#define IPIPE_ERR_FRAMING (2)
|
||||
// XXX: remove this
|
||||
#define IPIPE_ERR_TBD (0xffffffff)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright (C) 2013 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)
|
||||
|
||||
# For now thunderbolt implies thunderboot. May not
|
||||
# always be the case
|
||||
OPTIONS += WITH_THUNDERBOLT=1 \
|
||||
WITH_THUNDERBOOT=1
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/tbt_cp_crc.o \
|
||||
$(LOCAL_DIR)/tbt_control_port.o \
|
||||
$(LOCAL_DIR)/tbt_xdomain.o \
|
||||
$(LOCAL_DIR)/thunderboot.o \
|
||||
$(LOCAL_DIR)/uuid.o \
|
||||
$(LOCAL_DIR)/ipipe.o
|
||||
|
||||
ifeq ($(APPLICATION),SecureROM)
|
||||
OPTIONS += WITH_TBT_BOOT=1
|
||||
endif
|
||||
ifneq ($(filter $(PRODUCT),LLB,iBSS),)
|
||||
OPTIONS += WITH_TBT_BOOT=1
|
||||
endif
|
||||
|
||||
# SecureROM and iBSS-DFU support
|
||||
ifneq ($(filter $(OPTIONS), WITH_DFU_MODE=1), )
|
||||
|
||||
OPTIONS += \
|
||||
WITH_TBT_DFU=1 \
|
||||
WITH_TBT_MODE_DFU=1
|
||||
|
||||
endif
|
||||
|
||||
# Recovery mode support
|
||||
ifneq ($(filter $(OPTIONS), WITH_RECOVERY_MODE=1), )
|
||||
|
||||
OPTIONS += \
|
||||
WITH_TBT_MODE_RECOVERY=1
|
||||
|
||||
ifneq ($(BUILD),RELEASE)
|
||||
OPTIONS += \
|
||||
WITH_TBT_UPLOAD=0
|
||||
# XXX: should be a "1" above
|
||||
endif
|
||||
|
||||
MODULES += \
|
||||
lib/cbuf \
|
||||
lib/cksum
|
||||
|
||||
ALL_OBJS += \
|
||||
$(LOCAL_DIR)/thunderboot_debug.o
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,624 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <drivers/thunderbolt/nhi.h>
|
||||
#include <list.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/menu.h>
|
||||
#include <sys/task.h>
|
||||
#include "tbt_control_port.h"
|
||||
#include "tbt_cp_crc.h"
|
||||
#include "tbt_xdomain.h"
|
||||
#include "tbt_protocol.h"
|
||||
|
||||
#define CPDBG_DUMP_PACKET DEBUG_NEVER
|
||||
#define CPDBG_INIT DEBUG_INFO
|
||||
#define CPDBG_IO DEBUG_SPEW
|
||||
#define CPDBG_EVENT DEBUG_SPEW
|
||||
|
||||
struct xd_service {
|
||||
struct list_node list;
|
||||
// Callback function to handle incoming xdomain requests for this service
|
||||
tbt_xd_callback_t request_callback;
|
||||
// Callback function to handle incoming xdomain responses for this service
|
||||
tbt_xd_callback_t response_callback;
|
||||
// Private data passed in when the service was register
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct tbt_cp {
|
||||
nhi_t *nhi;
|
||||
uint8_t rx_buffer[256];
|
||||
uint8_t tx_buffer[256];
|
||||
uint8_t pdf_next_seq[TBT_CFG_MAX_PDF + 1];
|
||||
|
||||
bool shutdown;
|
||||
struct task *polling_task;
|
||||
|
||||
struct list_node xd_services;
|
||||
|
||||
uint32_t *read_req_buffer;
|
||||
uint32_t read_req_header;
|
||||
uint16_t read_req_quads;
|
||||
bool read_req_pending;
|
||||
struct task_event read_event;
|
||||
|
||||
uint32_t write_req_header;
|
||||
bool write_req_pending;
|
||||
struct task_event write_event;
|
||||
};
|
||||
|
||||
static void tbt_cp_process_xd_request(tbt_cp_t *cp, uint8_t *packet, size_t bytes);
|
||||
static void tbt_cp_process_xd_response(tbt_cp_t *cp, uint8_t *packet, size_t bytes);
|
||||
static void tbt_cp_process_read_response(tbt_cp_t *cp, uint8_t *packet, size_t bytes);
|
||||
static void tbt_cp_process_write_response(tbt_cp_t *cp, uint8_t *packet, size_t bytes);
|
||||
static void tbt_cp_process_plug_event(tbt_cp_t *cp, uint8_t *packet, size_t bytes);
|
||||
static void tbt_cp_process_error(tbt_cp_t *cp, uint8_t *packet, size_t bytes);
|
||||
static int tbt_cp_polling_task(void *arg);
|
||||
|
||||
static tbt_cp_t *g_tbt_cp;
|
||||
|
||||
void tbt_cp_dump_route_string(uint64_t route_string)
|
||||
{
|
||||
if (route_string & (1ULL << 63))
|
||||
printf("CM:");
|
||||
|
||||
printf("%02x", route_string & 0x3F);
|
||||
|
||||
for (int i = 1; i < 7; i++) {
|
||||
if (((route_string >> (i * 8)) & 0x3F) == 0)
|
||||
break;
|
||||
|
||||
printf(",%02x", (route_string >> (i * 8)) & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t tbt_cp_packet_get_route_string(const uint8_t *packet)
|
||||
{
|
||||
uint64_t route_string = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
route_string |= (uint64_t)packet[i] << ((8 - i - 1) * 8);
|
||||
|
||||
return route_string;
|
||||
}
|
||||
|
||||
void tbt_cp_packet_set_route_string(uint8_t *packet, uint64_t route_string)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
packet[i] = route_string >> ((8 - i - 1) * 8);
|
||||
}
|
||||
|
||||
void tbt_cp_dump_xdomain_message(uint8_t *packet, size_t bytes)
|
||||
{
|
||||
#if CPDBG_DUMP_PACKET <= DEBUG_LEVEL
|
||||
uint64_t route = tbt_cp_packet_get_route_string(packet);
|
||||
uint32_t header;
|
||||
uint32_t seq;
|
||||
uint32_t payload_len;
|
||||
char *len_message = "";
|
||||
size_t offset;
|
||||
|
||||
memcpy(&header, packet + 8, sizeof(uint32_t));
|
||||
header = htonl(header);
|
||||
seq = (header >> 27) & 0x3;
|
||||
payload_len = header & 0x3f;
|
||||
|
||||
if (payload_len * 4 > bytes - 12)
|
||||
len_message = " (INVALID)";
|
||||
|
||||
printf("Route String: ");
|
||||
tbt_cp_dump_route_string(route);
|
||||
printf("\n");
|
||||
printf("Length: 0x%02x%s\n", payload_len, len_message);
|
||||
printf("Sequence Num: 0x%02x\n", seq);
|
||||
printf("Payload:\n");
|
||||
|
||||
if (payload_len * 4 > bytes - 12)
|
||||
payload_len = (bytes - 12) / 4;
|
||||
|
||||
offset = 0;
|
||||
while (offset < payload_len * 4) {
|
||||
for (int i = 0; i < 16 && offset < payload_len * 4; i++, offset++)
|
||||
printf("%02x ", packet[offset + 12]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tbt_cp_dump_message(uint8_t *packet, size_t bytes)
|
||||
{
|
||||
#if CPDBG_DUMP_PACKET <= DEBUG_LEVEL
|
||||
uint64_t route = tbt_cp_packet_get_route_string(packet);
|
||||
uint32_t value;
|
||||
|
||||
printf("Route String: ");
|
||||
tbt_cp_dump_route_string(route);
|
||||
printf("\nPayload:\n");
|
||||
|
||||
for (size_t i = 0; i < bytes - 12; i += 4) {
|
||||
memcpy(&value, packet + 8 + i, 4);
|
||||
printf("%08x ", value);
|
||||
if (i % 16 == 12)
|
||||
printf("\n");
|
||||
}
|
||||
if ((bytes - 12) % 16 != 0)
|
||||
printf("\n");
|
||||
memcpy(&value, packet + bytes - 4, 4);
|
||||
printf("CRC: %08x\n", value);
|
||||
#endif
|
||||
}
|
||||
|
||||
tbt_cp_t *tbt_cp_create(nhi_t *nhi)
|
||||
{
|
||||
tbt_cp_t *cp;
|
||||
|
||||
cp = calloc(sizeof(*cp), 1);
|
||||
|
||||
dprintf(CPDBG_INIT, "tbt_cp: initializing\n");
|
||||
|
||||
cp->nhi = nhi;
|
||||
cp->xd_services = LIST_INITIAL_VALUE(cp->xd_services);
|
||||
|
||||
// Control messages are sent/received on ring 0
|
||||
// 16 buffers should be plenty. The packets are 256 bytes each
|
||||
// These functions will panic if someone already took the ring
|
||||
nhi_init_tx_ring(nhi, 0, 16, 256);
|
||||
nhi_init_rx_ring(nhi, 0, 16, 256);
|
||||
|
||||
event_init(&cp->read_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
event_init(&cp->write_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
|
||||
|
||||
// start a thread to handle packets for the control port
|
||||
cp->polling_task = task_create("tbt_polling", tbt_cp_polling_task, cp, 0);
|
||||
task_start(cp->polling_task);
|
||||
|
||||
// Pointer used for debug command access to the control port, assumes only 1 exists
|
||||
g_tbt_cp = cp;
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
void tbt_cp_quiesce_and_free(tbt_cp_t *cp)
|
||||
{
|
||||
cp->shutdown = true;
|
||||
|
||||
task_wait_on(cp->polling_task);
|
||||
|
||||
task_destroy(cp->polling_task);
|
||||
cp->polling_task = NULL;
|
||||
|
||||
nhi_disable_tx_ring(cp->nhi, 0);
|
||||
nhi_disable_rx_ring(cp->nhi, 0);
|
||||
|
||||
free(cp);
|
||||
}
|
||||
|
||||
void *tbt_cp_register_xd_service(tbt_cp_t *cp, tbt_xd_callback_t req_cb, tbt_xd_callback_t resp_cb, void *priv)
|
||||
{
|
||||
struct xd_service *new_service;
|
||||
|
||||
ASSERT(req_cb != NULL);
|
||||
ASSERT(resp_cb != NULL);
|
||||
|
||||
new_service = calloc(sizeof(*new_service), 1);
|
||||
new_service->request_callback = req_cb;
|
||||
new_service->response_callback = resp_cb;
|
||||
new_service->priv = priv;
|
||||
|
||||
list_add_tail(&cp->xd_services, &new_service->list);
|
||||
|
||||
return (void *)&new_service->list;
|
||||
}
|
||||
|
||||
void tbt_cp_unregister_xd_service(tbt_cp_t *cp, void *handle)
|
||||
{
|
||||
struct list_node *item = (struct list_node *)handle;
|
||||
struct xd_service *service;
|
||||
|
||||
if (!list_in_list(item))
|
||||
panic("unregistering nonregistered xdomain service");
|
||||
|
||||
service = containerof(item, struct xd_service, list);
|
||||
|
||||
list_delete(item);
|
||||
free(service);
|
||||
}
|
||||
|
||||
|
||||
bool tbt_cp_packet_available(tbt_cp_t *cp)
|
||||
{
|
||||
ASSERT(cp != NULL);
|
||||
|
||||
return nhi_rx_buffer_available(cp->nhi, 0);
|
||||
}
|
||||
|
||||
void tbt_cp_process_packet(tbt_cp_t *cp)
|
||||
{
|
||||
uint8_t eof_pdf;
|
||||
int32_t bytes;
|
||||
uint32_t packet_crc;
|
||||
uint32_t calc_crc;
|
||||
|
||||
bytes = nhi_rx_buffer(cp->nhi, 0, cp->rx_buffer, sizeof(cp->rx_buffer), NULL, &eof_pdf);
|
||||
|
||||
if (bytes < 0) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: error %d receiving packet\n", bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
// Packet must be big enough for header + CRC
|
||||
if (bytes < TBT_CFG_HEADER_LEN + 4) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: runt packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
calc_crc = tbt_cp_crc(cp->rx_buffer, bytes - 4);
|
||||
memcpy(&packet_crc, cp->rx_buffer + bytes - 4, 4);
|
||||
packet_crc = ntohl(packet_crc);
|
||||
if (calc_crc != packet_crc) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: CRC mismatch %x v %x\n", calc_crc, packet_crc);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (eof_pdf) {
|
||||
case TBT_CFG_READ_PDF:
|
||||
tbt_cp_dump_message(cp->rx_buffer, bytes);
|
||||
tbt_cp_process_read_response(cp, cp->rx_buffer, bytes - 4);
|
||||
break;
|
||||
case TBT_CFG_WRITE_PDF:
|
||||
tbt_cp_dump_message(cp->rx_buffer, bytes);
|
||||
tbt_cp_process_write_response(cp, cp->rx_buffer, bytes - 4);
|
||||
break;
|
||||
case TBT_CFG_PLUG_EVENT_PDF:
|
||||
tbt_cp_process_plug_event(cp, cp->rx_buffer, bytes - 4);
|
||||
break;
|
||||
case TBT_CFG_ERROR_PDF:
|
||||
tbt_cp_dump_message(cp->rx_buffer, bytes);
|
||||
tbt_cp_process_error(cp, cp->rx_buffer, bytes - 4);
|
||||
break;
|
||||
case TBT_CFG_XDOMAIN_REQUEST_PDF:
|
||||
tbt_cp_dump_xdomain_message(cp->rx_buffer, bytes);
|
||||
tbt_cp_process_xd_request(cp, cp->rx_buffer, bytes - 4);
|
||||
break;
|
||||
case TBT_CFG_XDOMAIN_RESPONSE_PDF:
|
||||
tbt_cp_dump_xdomain_message(cp->rx_buffer, bytes);
|
||||
tbt_cp_process_xd_response(cp, cp->rx_buffer, bytes - 4);
|
||||
break;
|
||||
default:
|
||||
dprintf(DEBUG_INFO, "tbt_cp: got unexpected PDF 0x%02x\n", eof_pdf);
|
||||
tbt_cp_dump_message(cp->rx_buffer, bytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Control port packets have a 2-bit sequence number. The OS stack increments the
|
||||
// sequence number per PDF, so we'll do the same. This function gets the next
|
||||
// sequence number and increments a 2-bit counter to provide for the subsequent value
|
||||
uint8_t tbt_cp_next_pdf_seq(tbt_cp_t *cp, uint8_t pdf)
|
||||
{
|
||||
uint8_t next;
|
||||
|
||||
ASSERT(cp != NULL);
|
||||
ASSERT(pdf <= TBT_CFG_MAX_PDF);
|
||||
|
||||
next = cp->pdf_next_seq[pdf];
|
||||
cp->pdf_next_seq[pdf] = (next + 1) & 3;
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
// Queues the given packet to be sent
|
||||
bool tbt_cp_send(tbt_cp_t *cp, uint8_t pdf, uint8_t *packet, size_t len)
|
||||
{
|
||||
uint32_t crc;
|
||||
nhi_sgl_t sgl[2];
|
||||
|
||||
ASSERT(len <= TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
|
||||
crc = htonl(tbt_cp_crc(packet, len));
|
||||
|
||||
sgl[0].buffer = packet;
|
||||
sgl[0].bytes = len;
|
||||
sgl[0].next = &sgl[1];
|
||||
sgl[1].buffer = &crc;
|
||||
sgl[1].bytes = sizeof(crc);
|
||||
sgl[1].next = NULL;
|
||||
|
||||
return nhi_send_sgl(cp->nhi, 0, sgl, pdf, pdf) == NHI_STATUS_OK;
|
||||
}
|
||||
|
||||
void tbt_cp_send_acknowledgement(tbt_cp_t *cp, uint64_t route_string, uint8_t port, uint8_t code)
|
||||
{
|
||||
uint32_t packet[3];
|
||||
|
||||
packet[0] = route_string >> 32;
|
||||
packet[1] = route_string;
|
||||
packet[2] = htonl((port << 8) | code);
|
||||
|
||||
tbt_cp_send(cp, TBT_CFG_ERROR_PDF, (uint8_t *)packet, sizeof(packet));
|
||||
}
|
||||
|
||||
bool tbt_cp_read(tbt_cp_t *cp, uint64_t topology_id, uint8_t space, uint8_t port, uint16_t index, uint32_t *buffer, uint16_t quadlets)
|
||||
{
|
||||
bool ret;
|
||||
uint32_t request[3];
|
||||
uint32_t header;
|
||||
uint8_t pdf = TBT_CFG_READ_PDF;
|
||||
uint8_t seq = tbt_cp_next_pdf_seq(cp, pdf);
|
||||
|
||||
ASSERT(quadlets < 60);
|
||||
|
||||
dprintf(CPDBG_IO, "tbt_cp: reading %d quads from %08llx %x:%02x:%04x\n", quadlets, topology_id, space, port, index);
|
||||
|
||||
request[0] = htonl(topology_id >> 32);
|
||||
request[1] = htonl(topology_id & 0xffffffff);
|
||||
header = 0;
|
||||
header |= (index & 0x1fff) << 0;
|
||||
header |= (quadlets & 0x3f) << 13;
|
||||
header |= (port & 0x3f) << 19;
|
||||
header |= (space & 0x3) << 25;
|
||||
header |= seq << 27;
|
||||
request[2] = htonl(header);
|
||||
|
||||
if (!tbt_cp_send(cp, pdf, (uint8_t *)request, sizeof(request)))
|
||||
return false;
|
||||
|
||||
// If we ever switch to interrupts, this will need to be reconsidered
|
||||
cp->read_req_header = header;
|
||||
cp->read_req_buffer = buffer;
|
||||
cp->read_req_quads = quadlets;
|
||||
cp->read_req_pending = true;
|
||||
|
||||
event_wait_timeout(&cp->read_event, 200000);
|
||||
|
||||
// if the read request is no longer pending, then tbt_cp_process_read_response saw a good response
|
||||
ret = !cp->read_req_pending;
|
||||
cp->read_req_pending = false;
|
||||
|
||||
if (ret) {
|
||||
for (unsigned i = 0; i < quadlets; i++)
|
||||
buffer[i] = ntohl(buffer[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tbt_cp_write(tbt_cp_t *cp, uint64_t topology_id, uint8_t space, uint8_t port, uint16_t index, const uint32_t *buffer, uint16_t quadlets)
|
||||
{
|
||||
uint8_t pdf = TBT_CFG_WRITE_PDF;
|
||||
uint8_t seq = tbt_cp_next_pdf_seq(cp, pdf);
|
||||
uint32_t header;
|
||||
uint32_t topo_high;
|
||||
uint32_t topo_low;
|
||||
uint32_t swapped;
|
||||
bool ret;
|
||||
|
||||
dprintf(CPDBG_IO, "tbt_cp: write %d quads to %016llx %x:%02x:%04x\n", quadlets, topology_id, space, port, index);
|
||||
|
||||
ASSERT(quadlets < 60);
|
||||
|
||||
topo_high = htonl(topology_id >> 32);
|
||||
topo_low = htonl(topology_id & 0xffffffff);
|
||||
header = 0;
|
||||
header |= (index & 0x1fff) << 0;
|
||||
header |= (quadlets & 0x3f) << 13;
|
||||
header |= (port & 0x3f) << 19;
|
||||
header |= (space & 0x3) << 25;
|
||||
header |= seq << 27;
|
||||
header = htonl(header);
|
||||
|
||||
memcpy(cp->tx_buffer + 0, &topo_high, 4);
|
||||
memcpy(cp->tx_buffer + 4, &topo_low, 4);
|
||||
memcpy(cp->tx_buffer + 8, &header, 4);
|
||||
for(uint16_t i = 0; i < quadlets; i++) {
|
||||
swapped = htonl(buffer[i]);
|
||||
memcpy(cp->tx_buffer + 12 + 4 * i, &swapped, 4);
|
||||
}
|
||||
|
||||
if (!tbt_cp_send(cp, pdf, cp->tx_buffer, 12 + 4 * quadlets))
|
||||
return false;
|
||||
|
||||
// If we ever use interrupts, this will need to be reordered compared to the send call
|
||||
cp->write_req_header = ntohl(header);
|
||||
cp->write_req_pending = true;
|
||||
|
||||
event_wait_timeout(&cp->write_event, 200000);
|
||||
|
||||
// If write_req_pending is false, then tbt_cp_process_write_response saw a good response
|
||||
ret = !cp->write_req_pending;
|
||||
cp->write_req_pending = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tbt_cp_process_read_response(tbt_cp_t *cp, uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint16_t len;
|
||||
uint32_t header;
|
||||
|
||||
if (!cp->read_req_pending) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: spurious read request response\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&header, packet + 8, 4);
|
||||
header = ntohl(header);
|
||||
len = (header >> 13) & 0x3f;
|
||||
|
||||
dprintf(CPDBG_IO, "tbt_cp: read response header=0x%08x bytes=%u\n", header, (unsigned)bytes);
|
||||
|
||||
// The far end is supposed to just copy the entire header back to us. They seem
|
||||
// to put garbage in the port number when we aren't reading port space, so
|
||||
// ignore it in the comparison
|
||||
if ((header & 0x1E07FFFF) != (cp->read_req_header & 0x1E07FFFF)) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: read request header mismatch, got %x expected %x mismatch %x\n", header, cp->read_req_header, header ^ cp->read_req_header);
|
||||
return;
|
||||
}
|
||||
|
||||
// Belt and suspenders check on the length, which is included in the header
|
||||
if (len != cp->read_req_quads) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: read request len mismatch, got %u expected %u\n", len, cp->read_req_quads);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytes != (len * 4) + 12) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: read request payload len mismatch, got %u expected %u\n", bytes, (len * 4) + 16);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(cp->read_req_buffer, packet + 12, cp->read_req_quads * 4);
|
||||
|
||||
cp->read_req_pending = false;
|
||||
|
||||
event_signal(&cp->read_event);
|
||||
}
|
||||
|
||||
static void tbt_cp_process_write_response(tbt_cp_t *cp, uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint32_t header;
|
||||
|
||||
if (!cp->write_req_pending) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: spurious write request response\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&header, packet + 8, 4);
|
||||
header = ntohl(header);
|
||||
|
||||
dprintf(CPDBG_IO, "tbt_cp: write response header=0x%08x bytes=%u\n", header, (unsigned)bytes);
|
||||
|
||||
// The far end is supposed to just copy the entire header back to us. We'll
|
||||
// mask off the reserved bits, but otherwise everything had better match
|
||||
if ((header & 0x1fffffff) != cp->write_req_header) {
|
||||
dprintf(DEBUG_CRITICAL, "tbt_cp: write request header mismatch, got %x expected %x\n", header, cp->read_req_header);
|
||||
return;
|
||||
}
|
||||
cp->write_req_pending = false;
|
||||
|
||||
event_signal(&cp->write_event);
|
||||
}
|
||||
|
||||
static void tbt_cp_process_plug_event(tbt_cp_t *cp, uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint64_t route_string;
|
||||
uint32_t details;
|
||||
uint8_t port;
|
||||
|
||||
// Send the acknowledgement back to the sender, clearing the CM bit
|
||||
route_string = tbt_cp_packet_get_route_string(packet);
|
||||
route_string &= ~(1ULL << 63);
|
||||
|
||||
memcpy(&details, packet + 8, sizeof(details));
|
||||
details = ntohl(details);
|
||||
|
||||
port = details & 0x3f;
|
||||
|
||||
dprintf(CPDBG_EVENT, "tbt_cp: Got plug event from %016llx for port %x\n", route_string, port);
|
||||
|
||||
tbt_cp_send_acknowledgement(cp, route_string, port, 7);
|
||||
}
|
||||
|
||||
static void tbt_cp_process_error(tbt_cp_t *cp, uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint64_t route_string;
|
||||
|
||||
// XXX
|
||||
dprintf(DEBUG_INFO, "Got an error packet. We should probably handle these?\n");
|
||||
|
||||
route_string = tbt_cp_packet_get_route_string(packet);
|
||||
}
|
||||
|
||||
static void tbt_cp_process_xd_request(tbt_cp_t *cp, uint8_t *packet, size_t bytes)
|
||||
{
|
||||
struct xd_service *service;
|
||||
|
||||
list_for_every_entry(&cp->xd_services, service, struct xd_service, list) {
|
||||
service->request_callback(cp, packet, bytes, service->priv);
|
||||
}
|
||||
}
|
||||
|
||||
static void tbt_cp_process_xd_response(tbt_cp_t *cp, uint8_t *packet, size_t bytes)
|
||||
{
|
||||
struct xd_service *service;
|
||||
|
||||
list_for_every_entry(&cp->xd_services, service, struct xd_service, list) {
|
||||
service->response_callback(cp, packet, bytes, service->priv);
|
||||
}
|
||||
}
|
||||
|
||||
static int tbt_cp_polling_task(void *arg)
|
||||
{
|
||||
tbt_cp_t *cp = (tbt_cp_t *)arg;
|
||||
|
||||
dprintf(CPDBG_INIT, "tbt_cp: polling task started\n");
|
||||
|
||||
while (!cp->shutdown) {
|
||||
while (tbt_cp_packet_available(cp)) {
|
||||
tbt_cp_process_packet(cp);
|
||||
}
|
||||
|
||||
task_yield();
|
||||
}
|
||||
|
||||
dprintf(CPDBG_INIT, "tbt_cp: polling task finished\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if DEBUG_BUILD
|
||||
static int do_tbtcp_debug_cmd(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int result = -1;
|
||||
const char *cmd = NULL;
|
||||
|
||||
// Nothing to do if there's no control port
|
||||
if (g_tbt_cp == NULL)
|
||||
return -1;
|
||||
|
||||
if (argc >= 2)
|
||||
cmd = args[1].str;
|
||||
|
||||
if (cmd != NULL) {
|
||||
if (strcmp(cmd, "read") == 0 && argc >= 6) {
|
||||
uint64_t topo_id = args[2].n;
|
||||
uint8_t space = args[3].n;
|
||||
uint8_t port = args[4].n;
|
||||
uint16_t index = args[5].n;
|
||||
uint32_t buffer;
|
||||
|
||||
if (tbt_cp_read(g_tbt_cp, topo_id, space, port, index, &buffer, 1)) {
|
||||
printf("0x%08x\n", buffer);
|
||||
result = 0;
|
||||
} else {
|
||||
printf("read failure\n");
|
||||
}
|
||||
} else if (strcmp(cmd, "write") == 0 && argc >= 7) {
|
||||
uint64_t topo_id = args[2].n;
|
||||
uint8_t space = args[3].n;
|
||||
uint8_t port = args[4].n;
|
||||
uint16_t index = args[5].n;
|
||||
uint32_t buffer = args[6].n;
|
||||
|
||||
if (tbt_cp_write(g_tbt_cp, topo_id, space, port, index, &buffer, 1)) {
|
||||
result = 0;
|
||||
} else {
|
||||
printf("write failure\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
MENU_COMMAND_DEBUG(tbtcp, do_tbtcp_debug_cmd, "TBT control port debug commands", NULL);
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef TBT_CONTROL_PORT_H
|
||||
#define TBT_CONTROL_PORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <drivers/thunderbolt/nhi.h>
|
||||
#include "uuid.h"
|
||||
|
||||
typedef struct tbt_cp tbt_cp_t;
|
||||
typedef void (*tbt_xd_callback_t)(tbt_cp_t *, const uint8_t *packet, size_t len, void *priv);
|
||||
|
||||
tbt_cp_t *tbt_cp_create(nhi_t *nhi);
|
||||
void tbt_cp_quiesce_and_free(tbt_cp_t *cp);
|
||||
void *tbt_cp_register_xd_service(tbt_cp_t *cp, tbt_xd_callback_t req_cb, tbt_xd_callback_t resp_cb, void *priv);
|
||||
void tbt_cp_unregister_xd_service(tbt_cp_t *cp, void *handle);
|
||||
|
||||
bool tbt_cp_read(tbt_cp_t *cp, uint64_t topology_id, uint8_t space, uint8_t port, uint16_t index, uint32_t *buffer, uint16_t quadlets);
|
||||
bool tbt_cp_write(tbt_cp_t *cp, uint64_t topology_id, uint8_t space, uint8_t port, uint16_t index, const uint32_t *buffer, uint16_t quadlets);
|
||||
|
||||
bool tbt_cp_packet_available(tbt_cp_t *cp);
|
||||
void tbt_cp_process_packet(tbt_cp_t *cp);
|
||||
uint8_t tbt_cp_next_pdf_seq(tbt_cp_t *cp, uint8_t pdf);
|
||||
bool tbt_cp_send(tbt_cp_t *cp, uint8_t pdf, uint8_t *packet, size_t len);
|
||||
|
||||
uint64_t tbt_cp_packet_get_route_string(const uint8_t *packet);
|
||||
void tbt_cp_packet_set_route_string(uint8_t *packet, uint64_t route_string);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/*****************************************************************/
|
||||
/* */
|
||||
/* CRC LOOKUP TABLE */
|
||||
/* ================ */
|
||||
/* The following CRC lookup table was generated automagically */
|
||||
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
||||
/* Program V1.0 using the following model parameters: */
|
||||
/* */
|
||||
/* Width : 4 bytes. */
|
||||
/* Poly : 0x1EDC6F41L */
|
||||
/* Reverse : TRUE. */
|
||||
/* */
|
||||
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
||||
/* see the document titled "A Painless Guide to CRC Error */
|
||||
/* Detection Algorithms" by Ross Williams */
|
||||
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
||||
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
uint32_t crctable[256] =
|
||||
{
|
||||
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
|
||||
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
|
||||
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
|
||||
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
|
||||
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
|
||||
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
|
||||
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
|
||||
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
|
||||
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
|
||||
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
|
||||
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
|
||||
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
|
||||
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
|
||||
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
|
||||
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
|
||||
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
|
||||
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
|
||||
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
|
||||
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
|
||||
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
|
||||
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
|
||||
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
|
||||
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
|
||||
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
|
||||
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
|
||||
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
|
||||
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
|
||||
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
|
||||
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
|
||||
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
|
||||
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
|
||||
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
|
||||
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
|
||||
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
|
||||
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
|
||||
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
|
||||
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
|
||||
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
|
||||
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
|
||||
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
|
||||
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
|
||||
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
|
||||
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
|
||||
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
|
||||
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
|
||||
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
|
||||
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
|
||||
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
|
||||
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
|
||||
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
|
||||
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
|
||||
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
|
||||
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
|
||||
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
|
||||
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
|
||||
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
|
||||
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
|
||||
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
|
||||
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
|
||||
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
|
||||
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
|
||||
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
|
||||
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
|
||||
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
|
||||
};
|
||||
|
||||
/*****************************************************************/
|
||||
/* End of CRC Lookup Table */
|
||||
/*****************************************************************/
|
||||
|
||||
/*****************************************************************/
|
||||
/* ComputeCRC */
|
||||
/*****************************************************************/
|
||||
|
||||
#define TB_INIT 0xFFFFFFFF
|
||||
#define TB_INIT_REFLECTED 0xFFFFFFFF
|
||||
#define TB_XOROT 0xFFFFFFFF
|
||||
|
||||
uint32_t tbt_cp_crc(const uint8_t *packet, uint32_t bytes)
|
||||
{
|
||||
uint32_t crc = TB_INIT_REFLECTED;
|
||||
while (bytes--)
|
||||
crc = crctable[(crc ^ *packet++) & 0xFFL] ^ (crc >> 8);
|
||||
return crc ^ TB_XOROT;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef TBT_CP_CRC
|
||||
#define TBT_CP_CRC
|
||||
|
||||
uint32_t tbt_cp_crc(const uint8_t *packet, uint32_t bytes);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef TBT_PROTOCOL_H
|
||||
#define TBT_PROTOCOL_H
|
||||
|
||||
#define TBT_CFG_SPACE_PATH (0)
|
||||
#define TBT_CFG_SPACE_PORT (1)
|
||||
#define TBT_CFG_SPACE_DEVICE (2)
|
||||
#define TBT_CFG_SPACE_COUNTERS (3)
|
||||
|
||||
#define TBT_CFG_READ_PDF (1)
|
||||
#define TBT_CFG_WRITE_PDF (2)
|
||||
#define TBT_CFG_ERROR_PDF (3)
|
||||
#define TBT_CFG_NOTIFY_ACK_PDF (4)
|
||||
#define TBT_CFG_PLUG_EVENT_PDF (5)
|
||||
#define TBT_CFG_XDOMAIN_REQUEST_PDF (6)
|
||||
#define TBT_CFG_XDOMAIN_RESPONSE_PDF (7)
|
||||
#define TBT_CFG_CM_OVERRIDE_PDF (8)
|
||||
#define TBT_CFG_RESET_PDF (9)
|
||||
#define TBT_CFG_PREPARE_TO_SLEEP_PDF (13)
|
||||
#define TBT_CFG_MAX_PDF (15)
|
||||
|
||||
#define TBT_CFG_HEADER_LEN (8)
|
||||
#define TBT_CFG_MAX_HEADER_AND_PAYLOAD (252) // XXX: Check this. Does NHI give us the first 4 bytes or not?
|
||||
|
||||
#define TBT_CFG_ROUTE_CM_MASK (1ULL << 63)
|
||||
#define TBT_CFG_ROUTE_STRING_MASK (~(3ULL << 62))
|
||||
|
||||
#define TBT_XD_REQUEST_HEADER_LEN (TBT_CFG_HEADER_LEN + 4 + 16 + 4)
|
||||
#define TBT_XD_RESPONSE_HEADER_LEN (TBT_XD_REQUEST_HEADER_LEN)
|
||||
#define TBT_XD_REQUEST_PROTOCOL_UUID_OFFSET (TBT_CFG_HEADER_LEN + 4)
|
||||
#define TBT_XD_REQUEST_PACKET_TYPE_OFFSET (TBT_CFG_HEADER_LEN + 4 + 16)
|
||||
#define TBT_XD_REQUEST_MAX_PAYLOAD (TBT_CFG_MAX_HEADER_AND_PAYLOAD - TBT_XD_REQUEST_HEADER_LEN)
|
||||
#define TBT_XD_RESPONSE_MAX_PAYLOAD (TBT_XD_REQUEST_MAX_PAYLOAD)
|
||||
|
||||
#define TBT_XD_UUID_REQUEST (1)
|
||||
#define TBT_XD_UUID_RESPONSE (2)
|
||||
#define TBT_XD_ROM_READ_REQUEST (3)
|
||||
#define TBT_XD_ROM_READ_RESPONSE (4)
|
||||
#define TBT_XD_ROM_CHANGED_REQUEST (5)
|
||||
#define TBT_XD_ROM_CHANGED_RESPONSE (6)
|
||||
#define TBT_XD_ERROR_RESPONSE (7)
|
||||
#define TBT_XD_UUID_V2_REQUEST (12)
|
||||
|
||||
#define TBT_XD_UUID_REQUEST_LEN (TBT_XD_REQUEST_HEADER_LEN)
|
||||
#define TBT_XD_UUID_RESPONSE_LEN (TBT_XD_RESPONSE_HEADER_LEN + 16)
|
||||
#define TBT_XD_ROM_CHANGED_REQUEST_LEN (TBT_XD_REQUEST_HEADER_LEN + 16)
|
||||
#define TBT_XD_ROM_CHANGED_RESPONSE_LEN (TBT_XD_RESPONSE_HEADER_LEN)
|
||||
#define TBT_XD_ROM_READ_REQUEST_LEN (TBT_XD_REQUEST_HEADER_LEN + 16 + 16 + 4)
|
||||
#define TBT_XD_ROM_READ_RESPONSE_DATA_OFFSET (16 + 16 + 4 + 4)
|
||||
#define TBT_XD_ROM_READ_RESPONSE_MAX_DATA (TBT_CFG_MAX_HEADER_AND_PAYLOAD - TBT_XD_RESPONSE_HEADER_LEN - TBT_XD_ROM_READ_RESPONSE_DATA_OFFSET)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef TBT_XD_PACKET_H
|
||||
#define TBT_XD_PACKET_H
|
||||
|
||||
#include <debug.h>
|
||||
#include "tbt_protocol.h"
|
||||
|
||||
// Sets the length of field of an xdomain packet. The length is given
|
||||
// in bytes, but must be a multiple of 4
|
||||
static inline void tbt_xd_packet_set_len(uint8_t *packet, uint8_t len)
|
||||
{
|
||||
ASSERT((len & 0x3) == 0);
|
||||
|
||||
// length (in dwords) is in bits 0-5 of the DWORD 2
|
||||
// clients include the length of the header when calling, but
|
||||
// the spec doesn't include the header length, so subtract 12
|
||||
packet[11] = (len - 12) >> 2;
|
||||
}
|
||||
|
||||
// Sets the sequence number field of an xdomain packet. The sequence
|
||||
// number must be a 2-bit number
|
||||
static inline void tbt_xd_packet_set_seq(uint8_t *packet, uint8_t seq)
|
||||
{
|
||||
ASSERT((seq & ~0x3U) == 0);
|
||||
|
||||
// sequence number is in bits 27-28 of the DWORD 2
|
||||
packet[8] = seq << 3;
|
||||
}
|
||||
|
||||
// Gets the sequence number field of an xdomain packet
|
||||
static inline uint8_t tbt_xd_packet_get_seq(const uint8_t *packet)
|
||||
{
|
||||
// sequence number is in bits 27-28 of the DWORD 2
|
||||
return (packet[8] >> 3) & 0x3;
|
||||
}
|
||||
|
||||
/* Sets the protocol UUID of the given xdomain request or response packet to the
|
||||
* given UUID in Thunderbolt network byte order
|
||||
*/
|
||||
static inline void tbt_xd_packet_set_protocol_uuid(uint8_t *packet, const uint8_t *uuid)
|
||||
{
|
||||
memcpy(packet + TBT_XD_REQUEST_PROTOCOL_UUID_OFFSET, uuid, 16);
|
||||
}
|
||||
|
||||
/* Compares the protocol UUID of the given xdomain request or response packet to the
|
||||
* given UUID in Thunderbolt network byte order
|
||||
*/
|
||||
static inline int tbt_xd_packet_compare_protocol_uuid(const uint8_t *packet, const uint8_t *uuid)
|
||||
{
|
||||
return memcmp(packet + TBT_XD_REQUEST_PROTOCOL_UUID_OFFSET, uuid, 16);
|
||||
}
|
||||
|
||||
static inline uint32_t tbt_xd_packet_get_type(const uint8_t *packet)
|
||||
{
|
||||
uint32_t packet_type;
|
||||
memcpy(&packet_type, packet + TBT_XD_REQUEST_PACKET_TYPE_OFFSET, sizeof(packet_type));
|
||||
return ntohl(packet_type);
|
||||
}
|
||||
|
||||
static inline void tbt_xd_packet_set_type(uint8_t *packet, uint32_t packet_type)
|
||||
{
|
||||
packet_type = htonl(packet_type);
|
||||
memcpy(packet + TBT_XD_REQUEST_PACKET_TYPE_OFFSET, &packet_type, sizeof(packet_type));
|
||||
}
|
||||
|
||||
static inline void tbt_xd_packet_get_payload(const uint8_t *packet, void *buffer, size_t offset, size_t bytes)
|
||||
{
|
||||
ASSERT(offset + bytes >= offset);
|
||||
ASSERT(offset < TBT_XD_REQUEST_MAX_PAYLOAD);
|
||||
ASSERT(offset + bytes <= TBT_XD_REQUEST_MAX_PAYLOAD);
|
||||
|
||||
memcpy(buffer, packet + TBT_XD_REQUEST_HEADER_LEN + offset, bytes);
|
||||
}
|
||||
|
||||
static inline void tbt_xd_packet_set_payload(uint8_t *packet, const void *buffer, size_t offset, size_t bytes)
|
||||
{
|
||||
ASSERT(offset + bytes >= offset);
|
||||
ASSERT(offset < TBT_XD_REQUEST_MAX_PAYLOAD);
|
||||
ASSERT(offset + bytes <= TBT_XD_REQUEST_MAX_PAYLOAD);
|
||||
|
||||
memcpy(packet + TBT_XD_REQUEST_HEADER_LEN + offset, buffer, bytes);
|
||||
}
|
||||
|
||||
static inline uint32_t tbt_xd_packet_get_payload_dword(const uint8_t *packet, size_t offset)
|
||||
{
|
||||
uint32_t dword;
|
||||
tbt_xd_packet_get_payload(packet, &dword, offset, sizeof(dword));
|
||||
|
||||
return ntohl(dword);
|
||||
}
|
||||
|
||||
static inline void tbt_xd_packet_set_payload_dword(uint8_t *packet, uint32_t dword, size_t offset)
|
||||
{
|
||||
dword = htonl(dword);
|
||||
tbt_xd_packet_set_payload(packet, &dword, offset, sizeof(dword));
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <list.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "tbt_control_port.h"
|
||||
#include "tbt_protocol.h"
|
||||
#include "tbt_xdomain.h"
|
||||
#include "tbt_xd_packet.h"
|
||||
#include "uuid.h"
|
||||
|
||||
struct tbt_xd_discovery {
|
||||
tbt_cp_t *control_port;
|
||||
void *xd_service_handle;
|
||||
|
||||
|
||||
// The configuration ROM
|
||||
const uint32_t *rom;
|
||||
// The number of 32-bit dwords in the ROM
|
||||
uint16_t rom_dwords;
|
||||
// The ROM generation number to send back with ROM read responses
|
||||
uint32_t rom_generation;
|
||||
// The xdomain discovery service's UUID in Thunderbolt network order
|
||||
uint8_t tbt_protocol_uuid[16];
|
||||
// The device's UUID in Thunderbolt network order
|
||||
uint8_t tbt_device_uuid[16];
|
||||
|
||||
// The route string from the last received xdomain request, used to figure
|
||||
// out where to send the ROM generation changed packet when a new boot stage
|
||||
// starts
|
||||
uint64_t last_route_string;
|
||||
|
||||
// Buffer used to assemble response packets before transmission
|
||||
uint8_t tx_buffer[TBT_CFG_MAX_HEADER_AND_PAYLOAD];
|
||||
};
|
||||
|
||||
static void tbt_xd_discovery_process_request(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv);
|
||||
static void tbt_xd_discovery_process_response(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv);
|
||||
static void tbt_xd_discovery_process_uuid_request(tbt_xd_discovery_t *xdd, const uint8_t *packet, size_t bytes, bool v2);
|
||||
static void tbt_xd_discovery_process_rom_changed_request(tbt_xd_discovery_t *xdd, const uint8_t *packet, size_t bytes);
|
||||
static void tbt_xd_discovery_process_rom_read_request(tbt_xd_discovery_t *xdd, const uint8_t *packet, size_t bytes);
|
||||
|
||||
static const uuid_t discovery_uuid = { 0xB638D70E, 0x42FF, 0x40BB, 0x97, 0xC2, { 0x90, 0xE2, 0xC0, 0xB2, 0xFF, 0x07 } };
|
||||
|
||||
tbt_xd_discovery_t *tbt_xd_discovery_create(tbt_cp_t *cp, const uint32_t *configuration_rom, uint16_t dwords, uint32_t generation)
|
||||
{
|
||||
tbt_xd_discovery_t *xdd;
|
||||
|
||||
xdd = calloc(sizeof(*xdd), 1);
|
||||
|
||||
xdd->control_port = cp;
|
||||
xdd->rom = configuration_rom;
|
||||
xdd->rom_dwords = dwords;
|
||||
xdd->rom_generation = generation;
|
||||
|
||||
uuid_host_to_tbt(&discovery_uuid, xdd->tbt_protocol_uuid);
|
||||
uuid_host_to_tbt(uuid_get_device_uuid(), xdd->tbt_device_uuid);
|
||||
|
||||
xdd->xd_service_handle = tbt_cp_register_xd_service(cp, tbt_xd_discovery_process_request, tbt_xd_discovery_process_response, xdd);
|
||||
|
||||
return xdd;
|
||||
}
|
||||
|
||||
void tbt_xd_discovery_quiesce_and_free(tbt_xd_discovery_t *xdd)
|
||||
{
|
||||
tbt_cp_unregister_xd_service(xdd->control_port, xdd->xd_service_handle);
|
||||
free(xdd);
|
||||
}
|
||||
|
||||
static void tbt_xd_discovery_process_request(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv)
|
||||
{
|
||||
tbt_xd_discovery_t *xdd = priv;
|
||||
uint32_t packet_type;
|
||||
uint64_t route_string;
|
||||
|
||||
// The packet needs to at least be big enough for a protocol UUID and packet type
|
||||
if (bytes < TBT_XD_REQUEST_HEADER_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make note of the route string in case we still need to send a ROM changed
|
||||
// request. We send this request to the last address that sent us an XD request
|
||||
route_string = tbt_cp_packet_get_route_string(packet);
|
||||
xdd->last_route_string = route_string & TBT_CFG_ROUTE_STRING_MASK;
|
||||
|
||||
// Now we can check to see if the packet has the discovery protocol's UUID
|
||||
if (tbt_xd_packet_compare_protocol_uuid(packet, xdd->tbt_protocol_uuid) != 0)
|
||||
return;
|
||||
|
||||
packet_type = tbt_xd_packet_get_type(packet);
|
||||
|
||||
switch (packet_type) {
|
||||
case TBT_XD_UUID_REQUEST:
|
||||
tbt_xd_discovery_process_uuid_request(xdd, packet, bytes, false);
|
||||
break;
|
||||
case TBT_XD_ROM_READ_REQUEST:
|
||||
tbt_xd_discovery_process_rom_read_request(xdd, packet, bytes);
|
||||
break;
|
||||
case TBT_XD_ROM_CHANGED_REQUEST:
|
||||
tbt_xd_discovery_process_rom_changed_request(xdd, packet, bytes);
|
||||
break;
|
||||
case TBT_XD_UUID_V2_REQUEST:
|
||||
tbt_xd_discovery_process_uuid_request(xdd, packet, bytes, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void tbt_xd_discovery_send_rom_changed_request(tbt_xd_discovery_t *xdd, uint64_t route)
|
||||
{
|
||||
uint8_t *request = xdd->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_REQUEST_PDF;
|
||||
uint8_t seq;
|
||||
size_t len;
|
||||
|
||||
memset(request, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
tbt_cp_packet_set_route_string(request, route);
|
||||
tbt_xd_packet_set_protocol_uuid(request, xdd->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(request, TBT_XD_ROM_CHANGED_REQUEST);
|
||||
|
||||
tbt_xd_packet_set_payload(request, xdd->tbt_device_uuid, 0, sizeof(xdd->tbt_device_uuid));
|
||||
|
||||
len = TBT_XD_ROM_CHANGED_REQUEST_LEN;
|
||||
|
||||
seq = tbt_cp_next_pdf_seq(xdd->control_port, pdf);
|
||||
tbt_xd_packet_set_len(request, len);
|
||||
tbt_xd_packet_set_seq(request, seq);
|
||||
|
||||
tbt_cp_send(xdd->control_port, pdf, request, len);
|
||||
}
|
||||
|
||||
static void tbt_xd_discovery_process_response(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv)
|
||||
{
|
||||
uint32_t packet_type;
|
||||
|
||||
packet_type = tbt_xd_packet_get_type(packet);
|
||||
|
||||
switch (packet_type) {
|
||||
case TBT_XD_ROM_CHANGED_RESPONSE:
|
||||
break;
|
||||
case TBT_XD_ERROR_RESPONSE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tbt_xd_discovery_process_rom_changed_request(tbt_xd_discovery_t *xdd, const uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint8_t *response = xdd->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
uint8_t seq;
|
||||
size_t len;
|
||||
uint64_t route;
|
||||
|
||||
route = tbt_cp_packet_get_route_string(packet) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
|
||||
memset(response, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
tbt_cp_packet_set_route_string(response, route);
|
||||
tbt_xd_packet_set_protocol_uuid(response, xdd->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(response, TBT_XD_ROM_CHANGED_RESPONSE);
|
||||
|
||||
len = TBT_XD_ROM_CHANGED_RESPONSE_LEN;
|
||||
|
||||
seq = tbt_cp_next_pdf_seq(xdd->control_port, pdf);
|
||||
tbt_xd_packet_set_len(response, len);
|
||||
tbt_xd_packet_set_seq(response, seq);
|
||||
|
||||
tbt_cp_send(xdd->control_port, pdf, response, len);
|
||||
}
|
||||
|
||||
static void tbt_xd_discovery_process_uuid_request(tbt_xd_discovery_t *xdd, const uint8_t *packet, size_t bytes, bool v2)
|
||||
{
|
||||
uint8_t *response = xdd->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
uint8_t seq;
|
||||
size_t len;
|
||||
uint64_t route;
|
||||
|
||||
route = tbt_cp_packet_get_route_string(packet) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
|
||||
memset(response, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
tbt_cp_packet_set_route_string(response, route);
|
||||
tbt_xd_packet_set_protocol_uuid(response, xdd->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(response, TBT_XD_UUID_RESPONSE);
|
||||
|
||||
tbt_xd_packet_set_payload(response, xdd->tbt_device_uuid, 0, sizeof(xdd->tbt_device_uuid));
|
||||
|
||||
len = TBT_XD_UUID_RESPONSE_LEN;
|
||||
|
||||
if (v2) {
|
||||
tbt_xd_packet_set_payload_dword(response, route >> 32, sizeof(xdd->tbt_device_uuid));
|
||||
tbt_xd_packet_set_payload_dword(response, route, sizeof(xdd->tbt_device_uuid) + 4);
|
||||
len += 8;
|
||||
}
|
||||
|
||||
seq = tbt_cp_next_pdf_seq(xdd->control_port, pdf);
|
||||
tbt_xd_packet_set_len(response, len);
|
||||
tbt_xd_packet_set_seq(response, seq);
|
||||
|
||||
tbt_cp_send(xdd->control_port, TBT_CFG_XDOMAIN_RESPONSE_PDF, response, len);
|
||||
}
|
||||
|
||||
static void tbt_xd_discovery_process_rom_read_request(tbt_xd_discovery_t *xdd, const uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint8_t *response = xdd->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
uint8_t seq;
|
||||
size_t len;
|
||||
uint64_t route;
|
||||
uint8_t host_uuid[16];
|
||||
uint32_t offset;
|
||||
uint16_t response_dwords;
|
||||
uint32_t offset_and_size;
|
||||
|
||||
if (bytes != TBT_XD_ROM_READ_REQUEST_LEN) {
|
||||
// XXX: Send error response
|
||||
return;
|
||||
}
|
||||
|
||||
route = tbt_cp_packet_get_route_string(packet) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
offset = tbt_xd_packet_get_payload_dword(packet, 32);
|
||||
|
||||
if (offset >= xdd->rom_dwords) {
|
||||
// XXX: Send error response
|
||||
return;
|
||||
}
|
||||
|
||||
response_dwords = TBT_XD_ROM_READ_RESPONSE_MAX_DATA / 4;
|
||||
if (xdd->rom_dwords < offset + response_dwords)
|
||||
response_dwords = xdd->rom_dwords - offset;
|
||||
|
||||
memset(response, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
tbt_cp_packet_set_route_string(response, route);
|
||||
tbt_xd_packet_set_protocol_uuid(response, xdd->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(response, TBT_XD_ROM_READ_RESPONSE);
|
||||
|
||||
// Grab the host UUID from the packet, we'll parrot it back in our response
|
||||
tbt_xd_packet_get_payload(packet, host_uuid, 0, sizeof(host_uuid));
|
||||
|
||||
// Packet starts with our UUID followed by the host's UUID
|
||||
tbt_xd_packet_set_payload(response, xdd->tbt_device_uuid, 0, sizeof(xdd->tbt_device_uuid));
|
||||
tbt_xd_packet_set_payload(response, host_uuid, 16, sizeof(host_uuid));
|
||||
|
||||
offset_and_size = offset | (xdd->rom_dwords << 16);
|
||||
tbt_xd_packet_set_payload_dword(response, offset_and_size, 32);
|
||||
tbt_xd_packet_set_payload_dword(response, xdd->rom_generation, 36);
|
||||
|
||||
for (int i = 0; i < response_dwords; i++)
|
||||
tbt_xd_packet_set_payload_dword(response, xdd->rom[offset + i], TBT_XD_ROM_READ_RESPONSE_DATA_OFFSET + i * 4);
|
||||
|
||||
len = TBT_XD_RESPONSE_HEADER_LEN + TBT_XD_ROM_READ_RESPONSE_DATA_OFFSET + 4 * response_dwords;
|
||||
seq = tbt_cp_next_pdf_seq(xdd->control_port, pdf);
|
||||
tbt_xd_packet_set_len(response, len);
|
||||
tbt_xd_packet_set_seq(response, seq);
|
||||
|
||||
tbt_cp_send(xdd->control_port, pdf, response, len);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef TBT_XDOMAIN_H
|
||||
#define TBT_XDOMAIN_H
|
||||
|
||||
#include "tbt_control_port.h"
|
||||
#include "uuid.h"
|
||||
|
||||
typedef struct tbt_xd_discovery tbt_xd_discovery_t;
|
||||
|
||||
tbt_xd_discovery_t *tbt_xd_discovery_create(tbt_cp_t *cp, const uint32_t *configuration_rom, uint16_t dwords, uint32_t generation);
|
||||
void tbt_xd_discovery_quiesce_and_free(tbt_xd_discovery_t *xdd);
|
||||
void tbt_xd_discovery_send_rom_changed_request(tbt_xd_discovery_t *xdd, uint64_t route);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright (C) 2013-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 <debug.h>
|
||||
#include <drivers/ipipe.h>
|
||||
#include <drivers/sha1.h>
|
||||
#include <drivers/thunderbolt/nhi.h>
|
||||
#include <drivers/thunderbolt/thunderboot.h>
|
||||
#include <lib/cbuf.h>
|
||||
#include <platform.h>
|
||||
#include <stdint.h>
|
||||
#include "tbt_control_port.h"
|
||||
#include "tbt_xdomain.h"
|
||||
#include "tbt_xd_packet.h"
|
||||
#include "thunderboot_protocol.h"
|
||||
#include "uuid.h"
|
||||
|
||||
// We expect the host to be reachable via an xdomain link
|
||||
// at port 3 of the TBT router connected to the SoC
|
||||
#define DEFAULT_HOST_ROUTE_STRING (3)
|
||||
|
||||
#if WITH_TBT_MODE_DFU
|
||||
#define PROTOCOL_SETTINGS (0x00000000)
|
||||
#else
|
||||
#define PROTOCOL_SETTINGS (0x00000001)
|
||||
#endif
|
||||
|
||||
#define TBT_SERVICE_OFFSET (0x22)
|
||||
|
||||
#define XDD_VAL(size) (0x76000000 | (size)) // 'v' - size must be 1, next word has value
|
||||
#define XDD_TXT(size) (0x74000000 | (size)) // 't' - next word has dword offset
|
||||
#define XDD_DIR(size) (0x44000000 | (size)) // 'D' - next word has dword offset
|
||||
|
||||
uint32_t TBT_XDP_DEFAULT[] =
|
||||
{
|
||||
0x55584401, // header
|
||||
0x00000018, // size of root directory in dwords
|
||||
'vend', 'orid', XDD_VAL(1), 0x00000001,
|
||||
'vend', 'orid', XDD_TXT(3), 0x0000001A,
|
||||
'devi', 'ceid', XDD_VAL(1), 0x00000001,
|
||||
'devi', 'ceid', XDD_TXT(5), 0x0000001D,
|
||||
'devi', 'cerv', XDD_VAL(1), 0x00000100, // >0x80000000 is OS
|
||||
'indd', 'evp\0', XDD_DIR(24), TBT_SERVICE_OFFSET,
|
||||
|
||||
'Appl', 'e In', 'c.\0\0', // vendorid text value
|
||||
'Appl', 'e Mo', 'bile', ' Dev', 'ice\0', // deviceid text value
|
||||
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, // UUID placeholder (comes from hash of values below)
|
||||
'prtc', 'id\0\0', XDD_VAL(1), 0x00000069,
|
||||
'prtc', 'vers', XDD_VAL(1), 0x00000001,
|
||||
'prtc', 'revs', XDD_VAL(1), 0x00000000,
|
||||
'prtc', 'stns', XDD_VAL(1), PROTOCOL_SETTINGS,
|
||||
0xffffffff, 0xffff766e, XDD_TXT(64), 0x0000003A, // USB serial number string pointer (0xffffffxx is vendor-specific range)
|
||||
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, // USB serial number string placeholder
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, //
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
};
|
||||
|
||||
#if APPLICATION_SECUREROM
|
||||
#define ROM_GENERATION (1)
|
||||
#elif APPLICATION_IBOOT && (PRODUCT_IBSS || PRODUCT_LLB)
|
||||
#define ROM_GENERATION (2)
|
||||
#elif APPLICATION_IBOOT && (PRODUCT_IBOOT)
|
||||
#define ROM_GENERATION (3)
|
||||
#elif APPLICATION_IBOOT && (PRODUCT_IBEC)
|
||||
#define ROM_GENERATION (4)
|
||||
#else
|
||||
#error "Unknown Configuration ROM generation"
|
||||
#endif
|
||||
|
||||
struct thunderboot {
|
||||
nhi_t *nhi;
|
||||
tbt_cp_t *cp;
|
||||
tbt_xd_discovery_t *xdd;
|
||||
void *xd_service_handle;
|
||||
|
||||
// Buffer for assembling outgoing packets
|
||||
uint8_t tx_buffer[256];
|
||||
|
||||
// UUIDs converted to network byte order
|
||||
uint8_t tbt_protocol_uuid[16];
|
||||
uint8_t tbt_device_uuid[16];
|
||||
|
||||
// handle for iPipe driver (protocol embedded in Thunderboot)
|
||||
ipipe_t *ipipe;
|
||||
|
||||
bool host_logged_in;
|
||||
// if the host logged in, the route string of the xdomain link it's on
|
||||
uint64_t host_route_string;
|
||||
// host's UUID in network byte order
|
||||
uint8_t host_uuid[16];
|
||||
|
||||
struct callout rom_changed_callout;
|
||||
};
|
||||
|
||||
static uuid_t thunderboot_protocol_uuid =
|
||||
{ 0xC7C43652, 0xAE71, 0x4577, 0xB2, 0x49, { 0x07, 0xB9, 0xC6, 0xED, 0x40, 0x52 } };
|
||||
|
||||
static thunderboot_t *g_thunderboot;
|
||||
|
||||
static void thunderboot_handle_xdomain_response(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv);
|
||||
static void thunderboot_handle_xdomain_request(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv);
|
||||
static void thunderboot_process_login_request(thunderboot_t *tb, const uint8_t *packet, size_t bytes);
|
||||
static void thunderboot_process_ping_request(thunderboot_t *tb, const uint8_t *packet, size_t bytes);
|
||||
static void thunderboot_process_ipipe_request(thunderboot_t *tb, const uint8_t *packet, size_t bytes);
|
||||
static void thunderboot_send_error_response(thunderboot_t *tb, const uint8_t *packet, uint32_t status);
|
||||
void thunderboot_ipipe_send(void *priv, const uint8_t *packet, uint32_t bytes);
|
||||
|
||||
thunderboot_t * thunderboot_init(pci_device_t bridge, uint32_t dart_id)
|
||||
{
|
||||
thunderboot_t *tb;
|
||||
nhi_t *nhi;
|
||||
const char *serial_number;
|
||||
const char *more_other;
|
||||
uint32_t idregs[5];
|
||||
uint32_t topology_id[2];
|
||||
uint8_t hash[CCSHA1_OUTPUT_SIZE];
|
||||
uuid_t service_uuid;
|
||||
|
||||
// Just one thunderboot device allowed right now
|
||||
ASSERT(g_thunderboot == NULL);
|
||||
|
||||
dprintf(DEBUG_INFO, "thunderboot: initializing\n");
|
||||
|
||||
nhi = nhi_init(bridge, dart_id);
|
||||
if (nhi == NULL) {
|
||||
dprintf(DEBUG_CRITICAL, "thunderboot: couldn't initialize NHI\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tb = calloc(sizeof(*tb), 1);
|
||||
tb->nhi = nhi;
|
||||
|
||||
// Set up the control port driver and point it at its NHI
|
||||
tb->cp = tbt_cp_create(tb->nhi);
|
||||
|
||||
if (tbt_cp_read(tb->cp, 0, TBT_CFG_SPACE_DEVICE, 0, 0, idregs, 5)) {
|
||||
dprintf(DEBUG_INFO, "thunderboot: nhi ID register 0x%x\n", idregs[0]);
|
||||
} else {
|
||||
dprintf(DEBUG_CRITICAL, "thunderboot: couldn't read device ID\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// The root switch's topology ID is 0. We just need to set it to valid
|
||||
// so that we can start getting messages
|
||||
topology_id[1] = 0x80000000U;
|
||||
topology_id[0] = 0;
|
||||
if (!tbt_cp_write(tb->cp, 0, TBT_CFG_SPACE_DEVICE, 0, 2, topology_id, 2)) {
|
||||
dprintf(DEBUG_CRITICAL, "thunderboot: couldn't set topology ID\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Stash some useful UUIDs
|
||||
uuid_host_to_tbt(&thunderboot_protocol_uuid, tb->tbt_protocol_uuid);
|
||||
uuid_host_to_tbt(uuid_get_device_uuid(), tb->tbt_device_uuid);
|
||||
|
||||
// Need to fill in runtime details in the xdomain discovery dictionary
|
||||
serial_number = platform_get_usb_serial_number_string(true);
|
||||
more_other = platform_get_usb_more_other_string(true);
|
||||
ASSERT(strlen(serial_number) < 128);
|
||||
ASSERT(strlen(more_other) < 128);
|
||||
strlcpy((char *)TBT_XDP_DEFAULT + sizeof(TBT_XDP_DEFAULT) - 256, serial_number, 128);
|
||||
strlcpy((char *)TBT_XDP_DEFAULT + sizeof(TBT_XDP_DEFAULT) - 256 + strlen(serial_number), more_other, 128);
|
||||
// strings need to be byte-swapped because... just don't ask
|
||||
for (size_t i = 0; i < 256 / sizeof(TBT_XDP_DEFAULT[0]); i++) {
|
||||
size_t offset = (sizeof(TBT_XDP_DEFAULT) - 256) / sizeof(TBT_XDP_DEFAULT[0]) + i;
|
||||
TBT_XDP_DEFAULT[offset] = htonl(TBT_XDP_DEFAULT[offset]);
|
||||
}
|
||||
|
||||
// To generate the thunderboot service's UUID, hash the contents of the service directory
|
||||
sha1_calculate(TBT_XDP_DEFAULT + TBT_SERVICE_OFFSET, sizeof(TBT_XDP_DEFAULT) - 4 * TBT_SERVICE_OFFSET, hash);
|
||||
uuid_generate_v5(hash, &service_uuid);
|
||||
memcpy((uint8_t *)(TBT_XDP_DEFAULT + TBT_SERVICE_OFFSET), &service_uuid, sizeof(service_uuid));
|
||||
|
||||
// route string where we send stuff until we get a login request
|
||||
tb->host_route_string = DEFAULT_HOST_ROUTE_STRING;
|
||||
|
||||
// Set up the xdomain discovery protocol driver and register is as an xdomain
|
||||
// packet handler with the control port driver
|
||||
tb->xdd = tbt_xd_discovery_create(tb->cp, TBT_XDP_DEFAULT, sizeof(TBT_XDP_DEFAULT)/4, ROM_GENERATION);
|
||||
// Let the host know that they should read our dictionary
|
||||
tbt_xd_discovery_send_rom_changed_request(tb->xdd, tb->host_route_string);
|
||||
|
||||
// Register ourselves as an xdomain packet handler
|
||||
tb->xd_service_handle = tbt_cp_register_xd_service(tb->cp, thunderboot_handle_xdomain_request, thunderboot_handle_xdomain_response, tb);
|
||||
|
||||
if (g_thunderboot == NULL)
|
||||
g_thunderboot = tb;
|
||||
|
||||
// XXX: use the right value here
|
||||
tb->ipipe = ipipe_init(THUNDERBOOT_SERIAL_REQUEST_MAX_DATA, &thunderboot_ipipe_send, tb);
|
||||
|
||||
dprintf(DEBUG_INFO, "thunderboot: initialized\n");
|
||||
|
||||
return tb;
|
||||
error:
|
||||
thunderboot_quiesce_and_free(tb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void thunderboot_quiesce_and_free(thunderboot_t *tb)
|
||||
{
|
||||
if (tb == NULL) {
|
||||
tb = g_thunderboot;
|
||||
if (tb == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf(DEBUG_INFO, "thunderboot: quiescing\n");
|
||||
|
||||
if (tb == g_thunderboot)
|
||||
g_thunderboot = NULL;
|
||||
|
||||
if (tb->xdd != NULL)
|
||||
tbt_xd_discovery_quiesce_and_free(tb->xdd);
|
||||
|
||||
if (tb->xd_service_handle != NULL)
|
||||
tbt_cp_unregister_xd_service(tb->cp, tb->xd_service_handle);
|
||||
|
||||
if (tb->ipipe != NULL)
|
||||
ipipe_quiesce_and_free(tb->ipipe);
|
||||
|
||||
if (tb->cp != NULL)
|
||||
tbt_cp_quiesce_and_free(tb->cp);
|
||||
|
||||
if (tb->nhi != NULL)
|
||||
nhi_quiesce_and_free(tb->nhi);
|
||||
|
||||
free(tb);
|
||||
}
|
||||
|
||||
static void thunderboot_handle_xdomain_request(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv)
|
||||
{
|
||||
thunderboot_t *tb = priv;
|
||||
uint32_t packet_type;
|
||||
|
||||
// The packet needs to at least be big enough for a protocol UUID and packet type
|
||||
if (bytes < THUNDERBOOT_REQUEST_HEADER_LEN)
|
||||
return;
|
||||
|
||||
// Now we can check to see if the packet has the discovery protocol's UUID
|
||||
if (tbt_xd_packet_compare_protocol_uuid(packet, tb->tbt_protocol_uuid) != 0)
|
||||
return;
|
||||
|
||||
packet_type = tbt_xd_packet_get_type(packet);
|
||||
|
||||
if (packet_type != THUNDERBOOT_LOGIN_REQUEST && !tb->host_logged_in) {
|
||||
dprintf(DEBUG_INFO, "thunderboot: packet received before login\n");
|
||||
thunderboot_send_error_response(tb, packet, THUNDERBOOT_STATUS_NOT_LOGGED_IN);
|
||||
}
|
||||
|
||||
switch (packet_type) {
|
||||
case THUNDERBOOT_LOGIN_REQUEST:
|
||||
thunderboot_process_login_request(tb, packet, bytes);
|
||||
break;
|
||||
case THUNDERBOOT_PING_REQUEST:
|
||||
thunderboot_process_ping_request(tb, packet, bytes);
|
||||
break;
|
||||
case THUNDERBOOT_IPIPE_REQUEST:
|
||||
thunderboot_process_ipipe_request(tb, packet, bytes);
|
||||
break;
|
||||
default:
|
||||
thunderboot_send_error_response(tb, packet, THUNDERBOOT_STATUS_UNKNOWN_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
void thunderboot_handle_xdomain_response(tbt_cp_t *cp, const uint8_t *packet, size_t bytes, void *priv)
|
||||
{
|
||||
// Right now we ignore all xdomain responses, but we need to provide
|
||||
// a handler to the xdomain driver
|
||||
}
|
||||
|
||||
/* Copies bytes from the packet into the supplied buffer. The offset is relative
|
||||
* to the first byte after the thunderboot packet type field */
|
||||
static void thunderboot_packet_get_payload(const uint8_t *packet, void *buffer, size_t offset, size_t bytes)
|
||||
{
|
||||
size_t xd_offset = offset + THUNDERBOOT_REQUEST_HEADER_LEN - TBT_XD_REQUEST_HEADER_LEN;
|
||||
tbt_xd_packet_get_payload(packet, buffer, xd_offset, bytes);
|
||||
}
|
||||
|
||||
/* Copies bytes into the packet from the supplied buffer. The offset is in bytes relative
|
||||
* to the first byte after the thunderboot packet type field */
|
||||
static void thunderboot_packet_set_payload(uint8_t *packet, const void *buffer, size_t offset, size_t bytes)
|
||||
{
|
||||
size_t xd_offset = offset + THUNDERBOOT_REQUEST_HEADER_LEN - TBT_XD_REQUEST_HEADER_LEN;
|
||||
tbt_xd_packet_set_payload(packet, buffer, xd_offset, bytes);
|
||||
}
|
||||
|
||||
/* Fetches a 32-bit dword from the packet, converting it from network to host
|
||||
* byte order. The offset is in bytes relative to the first byte after the
|
||||
* thunderboot packet type field */
|
||||
static uint32_t thunderboot_packet_get_payload_dword(const uint8_t *packet, size_t offset)
|
||||
{
|
||||
uint32_t dword;
|
||||
thunderboot_packet_get_payload(packet, &dword, offset, 4);
|
||||
return dword;
|
||||
}
|
||||
|
||||
/* Sets a 32-bit dword in the packet, converting it to network from host
|
||||
* byte order. The offset is in bytes relative to the first byte after the
|
||||
* thunderboot packet type field */
|
||||
static void thunderboot_packet_set_payload_dword(uint8_t *packet, uint32_t dword, size_t offset)
|
||||
{
|
||||
thunderboot_packet_set_payload(packet, &dword, offset, 4);
|
||||
}
|
||||
|
||||
/* Sets a byte in the packet. The offset is in bytes relative to the first byte after the
|
||||
* thunderboot packet type field */
|
||||
static void thunderboot_packet_set_payload_byte(uint8_t *packet, uint8_t byte, size_t offset)
|
||||
{
|
||||
thunderboot_packet_set_payload(packet, &byte, offset, 1);
|
||||
}
|
||||
|
||||
static void thunderboot_packet_get_sender_uuid(const uint8_t *packet, uint8_t *uuid)
|
||||
{
|
||||
tbt_xd_packet_get_payload(packet, uuid, 0, 16);
|
||||
}
|
||||
|
||||
static void thunderboot_packet_set_sender_uuid(uint8_t *packet, uint8_t *uuid)
|
||||
{
|
||||
tbt_xd_packet_set_payload(packet, uuid, 0, 16);
|
||||
}
|
||||
|
||||
static void thunderboot_send_error_response(thunderboot_t *tb, const uint8_t *request, uint32_t status)
|
||||
{
|
||||
uint8_t *packet = tb->tx_buffer;
|
||||
uint64_t route;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
uint8_t seq;
|
||||
size_t len;
|
||||
|
||||
route = tbt_cp_packet_get_route_string(request) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
seq = tbt_xd_packet_get_seq(request);
|
||||
|
||||
memset(packet, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
|
||||
tbt_cp_packet_set_route_string(packet, route);
|
||||
tbt_xd_packet_set_protocol_uuid(packet, tb->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(packet, THUNDERBOOT_ERROR_RESPONSE);
|
||||
thunderboot_packet_set_sender_uuid(packet, tb->tbt_device_uuid);
|
||||
|
||||
thunderboot_packet_set_payload_dword(packet, status, 0);
|
||||
|
||||
len = THUNDERBOOT_RESPONSE_HEADER_LEN + 4;
|
||||
tbt_xd_packet_set_len(packet, len);
|
||||
tbt_xd_packet_set_seq(packet, seq);
|
||||
|
||||
tbt_cp_send(tb->cp, pdf, packet, len);
|
||||
}
|
||||
|
||||
static void thunderboot_send_login_response(thunderboot_t *tb, uint64_t route, uint8_t seq, uint32_t status)
|
||||
{
|
||||
uint8_t *packet = tb->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
size_t len;
|
||||
|
||||
memset(packet, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
|
||||
tbt_cp_packet_set_route_string(packet, route);
|
||||
tbt_xd_packet_set_protocol_uuid(packet, tb->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(packet, THUNDERBOOT_LOGIN_RESPONSE);
|
||||
thunderboot_packet_set_sender_uuid(packet, tb->tbt_device_uuid);
|
||||
|
||||
thunderboot_packet_set_payload_dword(packet, status, 0);
|
||||
|
||||
len = THUNDERBOOT_RESPONSE_HEADER_LEN + 4;
|
||||
tbt_xd_packet_set_len(packet, len);
|
||||
tbt_xd_packet_set_seq(packet, seq);
|
||||
|
||||
tbt_cp_send(tb->cp, pdf, packet, len);
|
||||
}
|
||||
|
||||
static void thunderboot_process_login_request(thunderboot_t *tb, const uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint32_t status = THUNDERBOOT_STATUS_OK;
|
||||
uint64_t route;
|
||||
uint8_t destination_uuid[16];
|
||||
|
||||
route = tbt_cp_packet_get_route_string(packet) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
|
||||
if (bytes < THUNDERBOOT_LOGIN_REQUEST_LEN) {
|
||||
status = THUNDERBOOT_STATUS_MALFORMED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
thunderboot_packet_get_payload(packet, destination_uuid, 0, sizeof(destination_uuid));
|
||||
|
||||
if (memcmp(destination_uuid, tb->tbt_device_uuid, sizeof(destination_uuid)) != 0) {
|
||||
status = THUNDERBOOT_STATUS_WRONG_UUID;
|
||||
goto done;
|
||||
}
|
||||
|
||||
tb->host_route_string = route;
|
||||
thunderboot_packet_get_sender_uuid(packet, tb->host_uuid);
|
||||
|
||||
if (!tb->host_logged_in) {
|
||||
tb->host_logged_in = true;
|
||||
callout_dequeue(&tb->rom_changed_callout);
|
||||
}
|
||||
|
||||
done:
|
||||
thunderboot_send_login_response(tb, route, tbt_xd_packet_get_seq(packet), status);
|
||||
return;
|
||||
}
|
||||
|
||||
static void thunderboot_send_ping_response(thunderboot_t *tb, uint64_t route, uint8_t seq, uint64_t nonce, uint32_t status)
|
||||
{
|
||||
uint8_t *packet = tb->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
size_t len;
|
||||
|
||||
memset(packet, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
|
||||
tbt_cp_packet_set_route_string(packet, route);
|
||||
tbt_xd_packet_set_protocol_uuid(packet, tb->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(packet, THUNDERBOOT_PING_RESPONSE);
|
||||
thunderboot_packet_set_sender_uuid(packet, tb->tbt_device_uuid);
|
||||
|
||||
thunderboot_packet_set_payload_dword(packet, status, 0);
|
||||
thunderboot_packet_set_payload(packet, &nonce, 4, 8);
|
||||
|
||||
len = THUNDERBOOT_PING_RESPONSE_LEN;
|
||||
tbt_xd_packet_set_len(packet, len);
|
||||
tbt_xd_packet_set_seq(packet, seq);
|
||||
|
||||
tbt_cp_send(tb->cp, pdf, packet, len);
|
||||
}
|
||||
|
||||
static void thunderboot_process_ping_request(thunderboot_t *tb, const uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint32_t status = THUNDERBOOT_STATUS_OK;
|
||||
uint64_t route;
|
||||
uint64_t nonce = 0;
|
||||
|
||||
route = tbt_cp_packet_get_route_string(packet) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
|
||||
if (bytes < THUNDERBOOT_PING_REQUEST_LEN) {
|
||||
status = THUNDERBOOT_STATUS_MALFORMED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
thunderboot_packet_get_payload(packet, &nonce, 0, sizeof(nonce));
|
||||
|
||||
done:
|
||||
thunderboot_send_ping_response(tb, route, tbt_xd_packet_get_seq(packet), nonce, status);
|
||||
return;
|
||||
}
|
||||
|
||||
static void thunderboot_send_ipipe_response(thunderboot_t *tb, uint64_t route, uint8_t seq, uint32_t status)
|
||||
{
|
||||
uint8_t *packet = tb->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_RESPONSE_PDF;
|
||||
size_t len;
|
||||
|
||||
memset(packet, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
|
||||
tbt_cp_packet_set_route_string(packet, route);
|
||||
tbt_xd_packet_set_protocol_uuid(packet, tb->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(packet, THUNDERBOOT_IPIPE_RESPONSE);
|
||||
thunderboot_packet_set_sender_uuid(packet, tb->tbt_device_uuid);
|
||||
|
||||
thunderboot_packet_set_payload_dword(packet, status, 0);
|
||||
|
||||
len = THUNDERBOOT_RESPONSE_HEADER_LEN + 4;
|
||||
tbt_xd_packet_set_len(packet, len);
|
||||
tbt_xd_packet_set_seq(packet, seq);
|
||||
|
||||
tbt_cp_send(tb->cp, pdf, packet, len);
|
||||
}
|
||||
|
||||
static void thunderboot_process_ipipe_request(thunderboot_t *tb, const uint8_t *packet, size_t bytes)
|
||||
{
|
||||
uint64_t route;
|
||||
uint32_t payload_size;
|
||||
const uint8_t *payload;
|
||||
uint32_t status = THUNDERBOOT_STATUS_OK;
|
||||
|
||||
route = tbt_cp_packet_get_route_string(packet) & TBT_CFG_ROUTE_STRING_MASK;
|
||||
|
||||
if (bytes < THUNDERBOOT_IPIPE_REQUEST_HEADER_LEN) {
|
||||
status = THUNDERBOOT_STATUS_MALFORMED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
payload_size = bytes - THUNDERBOOT_IPIPE_REQUEST_HEADER_LEN;
|
||||
payload = packet + THUNDERBOOT_IPIPE_REQUEST_HEADER_LEN;
|
||||
|
||||
thunderboot_send_ipipe_response(tb, route, tbt_xd_packet_get_seq(packet), THUNDERBOOT_STATUS_OK);
|
||||
|
||||
ipipe_handle_packet(tb->ipipe, payload, payload_size);
|
||||
|
||||
return;
|
||||
|
||||
done:
|
||||
thunderboot_send_ipipe_response(tb, route, tbt_xd_packet_get_seq(packet), status);
|
||||
return;
|
||||
}
|
||||
|
||||
void thunderboot_ipipe_send(void *priv, const uint8_t *ipipe_packet, uint32_t bytes)
|
||||
{
|
||||
thunderboot_t *tb = (thunderboot_t *)priv;
|
||||
uint8_t *packet = tb->tx_buffer;
|
||||
uint8_t pdf = TBT_CFG_XDOMAIN_REQUEST_PDF;
|
||||
uint8_t seq;
|
||||
size_t len;
|
||||
|
||||
// XXX: better to queue up serial stuff and not call the send function
|
||||
// this can be deferred until after the ROM because we don't do serial for the ROM
|
||||
// Drop packets until the host has logged in
|
||||
if (!tb->host_logged_in)
|
||||
return;
|
||||
|
||||
memset(packet, 0, TBT_CFG_MAX_HEADER_AND_PAYLOAD);
|
||||
|
||||
tbt_cp_packet_set_route_string(packet, tb->host_route_string);
|
||||
tbt_xd_packet_set_protocol_uuid(packet, tb->tbt_protocol_uuid);
|
||||
tbt_xd_packet_set_type(packet, THUNDERBOOT_IPIPE_REQUEST);
|
||||
thunderboot_packet_set_sender_uuid(packet, tb->tbt_device_uuid);
|
||||
|
||||
thunderboot_packet_set_payload(packet, ipipe_packet, 0, bytes);
|
||||
|
||||
len = THUNDERBOOT_IPIPE_REQUEST_HEADER_LEN + ((bytes + 3) & ~3);
|
||||
seq = tbt_cp_next_pdf_seq(tb->cp, pdf);
|
||||
tbt_xd_packet_set_len(packet, len);
|
||||
tbt_xd_packet_set_seq(packet, seq);
|
||||
|
||||
tbt_cp_send(tb->cp, pdf, packet, len);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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 <drivers/thunderbolt/thunderboot.h>
|
||||
#include <sys/menu.h>
|
||||
#include <lib/cksum.h>
|
||||
#include <lib/env.h>
|
||||
#include <platform/memmap.h>
|
||||
|
||||
|
||||
//===================================================================================
|
||||
// Thunderboot command stuff
|
||||
//===================================================================================
|
||||
|
||||
#if defined(WITH_MENU) && WITH_MENU
|
||||
|
||||
#define USB_CMD_STRING_LEN 1024
|
||||
|
||||
#define USB_SERIAL_CMD_START 0xfc
|
||||
#define USB_SERIAL_CMD_END 0xfd
|
||||
#define USB_SERIAL_CMD_START_OFFSET 1
|
||||
#define USB_SERIAL_CMD_END_OFFSET 1
|
||||
#if WITH_DFU_UPLOAD
|
||||
#define MAX_NUM_CHAR_IN_INT (12)
|
||||
#endif // WITH_DFU_UPLOAD
|
||||
|
||||
static u_int8_t usb_cmd_string[USB_CMD_STRING_LEN] __attribute__ ((aligned(CPU_CACHELINE_SIZE)));
|
||||
|
||||
static int tbt_cmd_main(int argc, struct cmd_arg * args);
|
||||
|
||||
|
||||
MENU_COMMAND_DEVELOPMENT(tbt, tbt_cmd_main, "run a thunderboot command", NULL);
|
||||
|
||||
static void tbt_print_usage(const char * s)
|
||||
{
|
||||
printf("Usage: \n");
|
||||
printf("\t%s get <filename> [address] [amount] \n", s);
|
||||
printf( "\t%s get tftp://servername.com/file.bin [address] [amount] \n", s );
|
||||
#if WITH_DFU_UPLOAD
|
||||
printf( "\t%s put <filename> <amount> [address] \n", s );
|
||||
#endif // WITH_DFU_UPLOAD
|
||||
}
|
||||
|
||||
static int tbt_cmd_get(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
// XXX: We should check to see if a login has happened, and
|
||||
// fail if not
|
||||
|
||||
const char *prefix = "get ";
|
||||
const char * fileName = args[0].str;
|
||||
int notifyHostLen = strlen(prefix) + strlen(fileName) +
|
||||
USB_SERIAL_CMD_START_OFFSET + USB_SERIAL_CMD_END_OFFSET;
|
||||
|
||||
addr_t address = 0;
|
||||
uint64_t amount = 0;
|
||||
|
||||
if (notifyHostLen > USB_CMD_STRING_LEN) {
|
||||
printf("File length too long \n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
address = args[1].u;
|
||||
if(argc >= 3) {
|
||||
amount = args[2].u;
|
||||
}
|
||||
}
|
||||
|
||||
if (address == 0) {
|
||||
address = env_get_uint("loadaddr", DEFAULT_LOAD_ADDRESS);
|
||||
printf("Defaulting to address %p\n", address);
|
||||
}
|
||||
|
||||
if (amount > DEFAULT_RAMDISK_SIZE) {
|
||||
printf("Amount too large \n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (amount == 0) {
|
||||
amount = DEFAULT_RAMDISK_SIZE;
|
||||
}
|
||||
|
||||
if (!security_allow_memory((void *)address, amount)) {
|
||||
dprintf(DEBUG_INFO, "Permission Denied\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
thunderboot_transfer_prepare((void *)address, amount);
|
||||
bzero((void *)usb_cmd_string, USB_CMD_STRING_LEN);
|
||||
usb_cmd_string[0] = USB_SERIAL_CMD_START;
|
||||
memcpy(usb_cmd_string + USB_SERIAL_CMD_START_OFFSET, prefix, strlen(prefix));
|
||||
memcpy(usb_cmd_string + USB_SERIAL_CMD_START_OFFSET + strlen(prefix), fileName,
|
||||
notifyHostLen - USB_SERIAL_CMD_START_OFFSET - USB_SERIAL_CMD_END_OFFSET);
|
||||
usb_cmd_string[notifyHostLen - 1] = USB_SERIAL_CMD_END;
|
||||
|
||||
ret = thunderboot_serial_send_cmd_string(usb_cmd_string, notifyHostLen);
|
||||
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = thunderboot_transfer_wait();
|
||||
|
||||
if (ret <= 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
amount = ret;
|
||||
|
||||
printf("GET complete, amount %d\n", amount);
|
||||
|
||||
// set the file size and return code
|
||||
env_set_uint("filesize", amount, 0);
|
||||
|
||||
ret = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
error :
|
||||
printf("GET failed. \n");
|
||||
env_set_uint("filesize", 0, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int tbt_cmd_main(int argc, struct cmd_arg *args)
|
||||
{
|
||||
int ret = 0;
|
||||
const char *s = args[1].str;
|
||||
|
||||
if (s == NULL) {
|
||||
ret = -1;
|
||||
}
|
||||
else {
|
||||
if (strcmp(s, "get") == 0) {
|
||||
|
||||
if (argc < 3 || argc > 5) {
|
||||
goto print_usage;
|
||||
}
|
||||
|
||||
ret = tbt_cmd_get(argc - 2, args + 2);
|
||||
}
|
||||
#if 0 && WITH_DFU_UPLOAD
|
||||
else if(!strcmp(s, "put")) {
|
||||
|
||||
if((argc < 4) || (argc > 5)) {
|
||||
goto print_usage;
|
||||
}
|
||||
|
||||
ret = usb_cmd_put(args[2].str, args[3].u, (argc == 5) ? args[4].u : 0, 1);
|
||||
}
|
||||
#endif // WITH_DFU_UPLOAD
|
||||
else {
|
||||
goto print_usage;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
print_usage:
|
||||
tbt_print_usage( args[0].str );
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef THUNDERBOOT_PROTOCOL_H
|
||||
#define THUNDERBOOT_PROTOCOL_H
|
||||
|
||||
#include "tbt_protocol.h"
|
||||
|
||||
#define THUNDERBOOT_LOGIN_REQUEST (1)
|
||||
#define THUNDERBOOT_LOGIN_RESPONSE (2)
|
||||
#define THUNDERBOOT_PING_REQUEST (3)
|
||||
#define THUNDERBOOT_PING_RESPONSE (4)
|
||||
#define THUNDERBOOT_IPIPE_REQUEST (5)
|
||||
#define THUNDERBOOT_IPIPE_RESPONSE (6)
|
||||
#define THUNDERBOOT_ERROR_RESPONSE (255)
|
||||
|
||||
#define THUNDERBOOT_REQUEST_HEADER_LEN (TBT_XD_REQUEST_HEADER_LEN + 16)
|
||||
#define THUNDERBOOT_RESPONSE_HEADER_LEN (THUNDERBOOT_REQUEST_HEADER_LEN)
|
||||
|
||||
#define THUNDERBOOT_DATA_REQUEST_HEADER_LEN (THUNDERBOOT_REQUEST_HEADER_LEN + 4 + 4)
|
||||
#define THUNDERBOOT_DATA_REQUEST_MAX_DATA (TBT_CFG_MAX_HEADER_AND_PAYLOAD - THUNDERBOOT_DATA_REQUEST_HEADER_LEN)
|
||||
|
||||
#define THUNDERBOOT_SERIAL_REQUEST_HEADER_LEN (THUNDERBOOT_REQUEST_HEADER_LEN + 4)
|
||||
#define THUNDERBOOT_SERIAL_REQUEST_MAX_DATA (TBT_CFG_MAX_HEADER_AND_PAYLOAD - THUNDERBOOT_SERIAL_REQUEST_HEADER_LEN)
|
||||
|
||||
#define THUNDERBOOT_IPIPE_REQUEST_HEADER_LEN (THUNDERBOOT_REQUEST_HEADER_LEN)
|
||||
#define THUNDERBOOT_IPIPE_REQUEST_MAX_DATA (TBT_CFG_MAX_HEADER_AND_PAYLOAD - THUNDERBOOT_IPIPE_REQUEST_HEADER_LEN)
|
||||
|
||||
#define THUNDERBOOT_LOGIN_REQUEST_LEN (THUNDERBOOT_REQUEST_HEADER_LEN + 16)
|
||||
|
||||
#define THUNDERBOOT_PING_REQUEST_LEN (THUNDERBOOT_REQUEST_HEADER_LEN + 8)
|
||||
#define THUNDERBOOT_PING_RESPONSE_LEN (THUNDERBOOT_PING_REQUEST_LEN + 4 + 8)
|
||||
|
||||
#define THUNDERBOOT_STATUS_OK (0)
|
||||
#define THUNDERBOOT_STATUS_MALFORMED (1)
|
||||
#define THUNDERBOOT_STATUS_UNKNOWN_TYPE (2)
|
||||
#define THUNDERBOOT_STATUS_WRONG_MODE (3)
|
||||
#define THUNDERBOOT_STATUS_NOT_LOGGED_IN (4)
|
||||
#define THUNDERBOOT_STATUS_WRONG_UUID (5)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2013-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 <drivers/sha1.h>
|
||||
#include <platform.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "uuid.h"
|
||||
|
||||
const uuid_t url_namespace_uuid = { 0x6ba7b811, 0x9dad, 0x11d1, 0x80, 0xb4, { 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } };
|
||||
|
||||
void uuid_host_to_network(const uuid_t *source, uint8_t *dest)
|
||||
{
|
||||
dest[0] = source->time_low >> 0;
|
||||
dest[1] = source->time_low >> 8;
|
||||
dest[2] = source->time_low >> 16;
|
||||
dest[3] = source->time_low >> 24;
|
||||
dest[4] = source->time_mid >> 0;
|
||||
dest[5] = source->time_mid >> 8;
|
||||
dest[6] = source->time_hi_and_version >> 0;
|
||||
dest[7] = source->time_hi_and_version >> 8;
|
||||
dest[8] = source->clock_seq_hi_and_reserved;
|
||||
dest[9] = source->clock_seq_low;
|
||||
dest[10] = source->node[0];
|
||||
dest[11] = source->node[1];
|
||||
dest[12] = source->node[2];
|
||||
dest[13] = source->node[3];
|
||||
dest[14] = source->node[4];
|
||||
dest[15] = source->node[5];
|
||||
}
|
||||
|
||||
void uuid_network_to_host(const uint8_t *src, uuid_t *dest)
|
||||
{
|
||||
dest->time_low = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
|
||||
dest->time_mid = src[4] << 8 | src[5];
|
||||
dest->time_hi_and_version = src[6] << 8 | src[7];
|
||||
dest->clock_seq_hi_and_reserved = src[8];
|
||||
dest->clock_seq_low = src[9];
|
||||
dest->node[0] = src[10];
|
||||
dest->node[1] = src[11];
|
||||
dest->node[2] = src[12];
|
||||
dest->node[3] = src[13];
|
||||
dest->node[4] = src[14];
|
||||
dest->node[5] = src[15];
|
||||
}
|
||||
|
||||
void uuid_host_to_tbt(const uuid_t *source, uint8_t *dest)
|
||||
{
|
||||
uint32_t dword;
|
||||
|
||||
memcpy(dest, (uint8_t *)source, 4);
|
||||
|
||||
memcpy(dest + 6, (uint8_t *)source + 4, 2);
|
||||
memcpy(dest + 4, (uint8_t *)source + 6, 2);
|
||||
|
||||
memcpy(&dword, (uint8_t *)source + 8, 4);
|
||||
dword = htonl(dword);
|
||||
memcpy(dest + 8, &dword, 4);
|
||||
|
||||
memcpy(&dword, (uint8_t *)source + 12, 4);
|
||||
dword = htonl(dword);
|
||||
memcpy(dest + 12, &dword, 4);
|
||||
}
|
||||
|
||||
void uuid_tbt_to_host(const uint8_t *source, uuid_t *dest)
|
||||
{
|
||||
uint32_t dword;
|
||||
|
||||
memcpy((uint8_t *)dest, source, 4);
|
||||
|
||||
memcpy((uint8_t *)dest + 6, source + 4, 2);
|
||||
memcpy((uint8_t *)dest + 4, source + 6, 2);
|
||||
|
||||
memcpy(&dword, source + 8, 4);
|
||||
dword = htonl(dword);
|
||||
memcpy((uint8_t *)dest + 8, &dword, 4);
|
||||
|
||||
memcpy(&dword, source + 12, 4);
|
||||
dword = htonl(dword);
|
||||
memcpy((uint8_t *)dest + 12, &dword, 4);
|
||||
}
|
||||
|
||||
void uuid_generate_v5(const uint8_t *hash, uuid_t *dest)
|
||||
{
|
||||
uuid_network_to_host(hash, dest);
|
||||
dest->time_hi_and_version &= 0x0FFF;
|
||||
dest->time_hi_and_version |= 5 << 12;
|
||||
dest->clock_seq_hi_and_reserved &= 0x3F;
|
||||
dest->clock_seq_hi_and_reserved |= 0x80;
|
||||
}
|
||||
|
||||
const uuid_t *uuid_get_device_uuid(void)
|
||||
{
|
||||
uint8_t bytes_to_hash[16 + 33 + 8 + 16 + 1];
|
||||
uint8_t hash[CCSHA1_OUTPUT_SIZE];
|
||||
char *url;
|
||||
|
||||
static bool uuid_generated = false;
|
||||
static uuid_t uuid;
|
||||
|
||||
if (!uuid_generated) {
|
||||
uuid_generated = true;
|
||||
|
||||
uuid_host_to_network(&url_namespace_uuid, bytes_to_hash);
|
||||
url = (char *)bytes_to_hash + 16;
|
||||
|
||||
const char *format_str = "http://coreos.apple.com/socuuid/%08x/%016llx";
|
||||
snprintf(url, sizeof(bytes_to_hash) - 16, format_str, platform_get_chip_id(), platform_get_ecid_id());
|
||||
|
||||
sha1_calculate(bytes_to_hash, sizeof(bytes_to_hash) - 1, hash);
|
||||
|
||||
uuid_generate_v5(hash, &uuid);
|
||||
}
|
||||
|
||||
return &uuid;
|
||||
}
|
||||
|
||||
void uuid_print(const uuid_t *src)
|
||||
{
|
||||
printf("%08x-%04x-%04x-%02x%02x-", src->time_low, src->time_mid, src->time_hi_and_version,
|
||||
src->clock_seq_hi_and_reserved, src->clock_seq_low);
|
||||
for (int i = 0; i < 6; i++)
|
||||
printf("%02x", src->node[i]);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.
|
||||
*/
|
||||
#ifndef UUID_H
|
||||
#define UUID_H
|
||||
|
||||
typedef struct uuid {
|
||||
uint32_t time_low;
|
||||
uint16_t time_mid;
|
||||
uint16_t time_hi_and_version;
|
||||
uint8_t clock_seq_hi_and_reserved;
|
||||
uint8_t clock_seq_low;
|
||||
uint8_t node[6];
|
||||
} uuid_t;
|
||||
|
||||
void uuid_host_to_network(const uuid_t *source, uint8_t *dest);
|
||||
void uuid_network_to_host(const uint8_t *src, uuid_t *dest);
|
||||
void uuid_host_to_tbt(const uuid_t *source, uint8_t *dest);
|
||||
void uuid_tbt_to_host(const uint8_t *source, uuid_t *dest);
|
||||
void uuid_generate_v5(const uint8_t *hash, uuid_t *dest);
|
||||
const uuid_t *uuid_get_device_uuid(void);
|
||||
void uuid_print(const uuid_t *src);
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user