2018-09-07 16:00:13 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 naehrwert
|
|
|
|
* Copyright (c) 2018 Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-08-18 17:59:33 +01:00
|
|
|
#include "tsec.h"
|
|
|
|
#include "di.h"
|
|
|
|
#include "timers.h"
|
|
|
|
#include "car.h"
|
|
|
|
|
|
|
|
static int tsec_dma_wait_idle()
|
|
|
|
{
|
|
|
|
volatile tegra_tsec_t *tsec = tsec_get_regs();
|
|
|
|
uint32_t timeout = (get_time_ms() + 10000);
|
|
|
|
|
|
|
|
while (!(tsec->FALCON_DMATRFCMD & 2))
|
|
|
|
if (get_time_ms() > timeout)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tsec_dma_phys_to_flcn(bool is_imem, uint32_t flcn_offset, uint32_t phys_offset)
|
|
|
|
{
|
|
|
|
volatile tegra_tsec_t *tsec = tsec_get_regs();
|
|
|
|
uint32_t cmd = 0;
|
|
|
|
|
|
|
|
if (!is_imem)
|
|
|
|
cmd = 0x600;
|
|
|
|
else
|
|
|
|
cmd = 0x10;
|
|
|
|
|
|
|
|
tsec->FALCON_DMATRFMOFFS = flcn_offset;
|
|
|
|
tsec->FALCON_DMATRFFBOFFS = phys_offset;
|
|
|
|
tsec->FALCON_DMATRFCMD = cmd;
|
|
|
|
|
|
|
|
return tsec_dma_wait_idle();
|
|
|
|
}
|
|
|
|
|
2018-11-26 00:22:47 +00:00
|
|
|
int tsec_get_key(uint8_t *key, uint32_t rev, const void *tsec_fw, size_t tsec_fw_size)
|
2018-08-18 17:59:33 +01:00
|
|
|
{
|
|
|
|
volatile tegra_tsec_t *tsec = tsec_get_regs();
|
|
|
|
|
|
|
|
/* Enable clocks. */
|
|
|
|
clkrst_reboot(CARDEVICE_HOST1X);
|
|
|
|
clkrst_reboot(CARDEVICE_TSEC);
|
|
|
|
clkrst_reboot(CARDEVICE_SOR_SAFE);
|
|
|
|
clkrst_reboot(CARDEVICE_SOR0);
|
|
|
|
clkrst_reboot(CARDEVICE_SOR1);
|
|
|
|
clkrst_reboot(CARDEVICE_KFUSE);
|
|
|
|
|
|
|
|
/* Configure Falcon. */
|
|
|
|
tsec->FALCON_DMACTL = 0;
|
|
|
|
tsec->FALCON_IRQMSET = 0xFFF2;
|
|
|
|
tsec->FALCON_IRQDEST = 0xFFF0;
|
|
|
|
tsec->FALCON_ITFEN = 3;
|
|
|
|
|
|
|
|
if (!tsec_dma_wait_idle())
|
|
|
|
{
|
|
|
|
/* Disable clocks. */
|
|
|
|
clkrst_disable(CARDEVICE_KFUSE);
|
|
|
|
clkrst_disable(CARDEVICE_SOR1);
|
|
|
|
clkrst_disable(CARDEVICE_SOR0);
|
|
|
|
clkrst_disable(CARDEVICE_SOR_SAFE);
|
|
|
|
clkrst_disable(CARDEVICE_TSEC);
|
|
|
|
clkrst_disable(CARDEVICE_HOST1X);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load firmware. */
|
|
|
|
tsec->FALCON_DMATRFBASE = (uint32_t)tsec_fw >> 8;
|
2018-11-26 00:22:47 +00:00
|
|
|
for (uint32_t addr = 0; addr < tsec_fw_size; addr += 0x100)
|
2018-08-18 17:59:33 +01:00
|
|
|
{
|
|
|
|
if (!tsec_dma_phys_to_flcn(true, addr, addr))
|
|
|
|
{
|
|
|
|
/* Disable clocks. */
|
|
|
|
clkrst_disable(CARDEVICE_KFUSE);
|
|
|
|
clkrst_disable(CARDEVICE_SOR1);
|
|
|
|
clkrst_disable(CARDEVICE_SOR0);
|
|
|
|
clkrst_disable(CARDEVICE_SOR_SAFE);
|
|
|
|
clkrst_disable(CARDEVICE_TSEC);
|
|
|
|
clkrst_disable(CARDEVICE_HOST1X);
|
|
|
|
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unknown host1x write. */
|
|
|
|
MAKE_HOST1X_REG(0x3300) = 0x34C2E1DA;
|
|
|
|
|
|
|
|
/* Execute firmware. */
|
|
|
|
tsec->FALCON_SCRATCH1 = 0;
|
|
|
|
tsec->FALCON_SCRATCH0 = rev;
|
|
|
|
tsec->FALCON_BOOTVEC = 0;
|
|
|
|
tsec->FALCON_CPUCTL = 2;
|
|
|
|
|
|
|
|
if (!tsec_dma_wait_idle())
|
|
|
|
{
|
|
|
|
/* Disable clocks. */
|
|
|
|
clkrst_disable(CARDEVICE_KFUSE);
|
|
|
|
clkrst_disable(CARDEVICE_SOR1);
|
|
|
|
clkrst_disable(CARDEVICE_SOR0);
|
|
|
|
clkrst_disable(CARDEVICE_SOR_SAFE);
|
|
|
|
clkrst_disable(CARDEVICE_TSEC);
|
|
|
|
clkrst_disable(CARDEVICE_HOST1X);
|
|
|
|
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t timeout = (get_time_ms() + 2000);
|
|
|
|
while (!tsec->FALCON_SCRATCH1)
|
|
|
|
{
|
|
|
|
if (get_time_ms() > timeout)
|
|
|
|
{
|
|
|
|
/* Disable clocks. */
|
|
|
|
clkrst_disable(CARDEVICE_KFUSE);
|
|
|
|
clkrst_disable(CARDEVICE_SOR1);
|
|
|
|
clkrst_disable(CARDEVICE_SOR0);
|
|
|
|
clkrst_disable(CARDEVICE_SOR_SAFE);
|
|
|
|
clkrst_disable(CARDEVICE_TSEC);
|
|
|
|
clkrst_disable(CARDEVICE_HOST1X);
|
|
|
|
|
|
|
|
return -4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tsec->FALCON_SCRATCH1 != 0xB0B0B0B0)
|
|
|
|
{
|
|
|
|
/* Disable clocks. */
|
|
|
|
clkrst_disable(CARDEVICE_KFUSE);
|
|
|
|
clkrst_disable(CARDEVICE_SOR1);
|
|
|
|
clkrst_disable(CARDEVICE_SOR0);
|
|
|
|
clkrst_disable(CARDEVICE_SOR_SAFE);
|
|
|
|
clkrst_disable(CARDEVICE_TSEC);
|
|
|
|
clkrst_disable(CARDEVICE_HOST1X);
|
|
|
|
|
|
|
|
return -5;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unknown host1x write. */
|
|
|
|
MAKE_HOST1X_REG(0x3300) = 0;
|
|
|
|
|
|
|
|
/* Fetch result from SOR1. */
|
|
|
|
uint32_t tmp[0x4] = {0};
|
|
|
|
tmp[0] = SOR1_DP_HDCP_BKSV_LSB;
|
|
|
|
tmp[1] = SOR1_TMDS_HDCP_BKSV_LSB;
|
|
|
|
tmp[2] = SOR1_TMDS_HDCP_CN_MSB;
|
|
|
|
tmp[3] = SOR1_TMDS_HDCP_CN_LSB;
|
|
|
|
|
|
|
|
/* Clear SOR1 registers. */
|
|
|
|
SOR1_DP_HDCP_BKSV_LSB = 0;
|
|
|
|
SOR1_TMDS_HDCP_BKSV_LSB = 0;
|
|
|
|
SOR1_TMDS_HDCP_CN_MSB = 0;
|
|
|
|
SOR1_TMDS_HDCP_CN_LSB = 0;
|
|
|
|
|
|
|
|
/* Copy back the key. */
|
|
|
|
memcpy(key, &tmp, 0x10);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|