first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

+467
View File
@@ -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);
+22
View File
@@ -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
+61
View File
@@ -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
+624
View File
@@ -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);
+37
View File
@@ -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
+109
View File
@@ -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;
}
+16
View File
@@ -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
+61
View File
@@ -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
+108
View File
@@ -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
+265
View File
@@ -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);
}
+23
View File
@@ -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
+549
View File
@@ -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);
}
+176
View File
@@ -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
+131
View File
@@ -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]);
}
+31
View File
@@ -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