iBoot/drivers/thunderbolt/tbt_xdomain.c

266 lines
8.8 KiB
C

/*
* 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);
}