hardware/liverpool: Separate SAM/SMU into dedicated engines

This commit is contained in:
Alexandro Sanchez Bach 2021-11-01 13:38:08 +01:00
parent bc67ad6b8b
commit 1541e6eaad
7 changed files with 420 additions and 105 deletions

View file

@ -45,8 +45,11 @@ ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/aeolia)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/aeolia/uart)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool/gca)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool/gmc)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool/oss)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool/smu)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/hardware/liverpool/sam)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/software)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/software/sbl)
ORBITAL_SOURCES_APPEND(${ORBITAL_DIR_SOURCES}/orbital/ui)

View file

@ -22,9 +22,8 @@
#include "gmc/gmc_7_1_sh_mask.h"
#include "oss/oss_2_0_d.h"
#include "oss/oss_2_0_sh_mask.h"
#include "smu/smu_7_1_2_d.h"
#include "smu/smu_7_1_2_sh_mask.h"
#include "sam/sam.h"
constexpr auto GC_MMIO_PCI = OffsetRange(0x0000, 0x100);
// Logging
#define DEBUG_GC 0
@ -40,7 +39,9 @@ do { \
LiverpoolGCDevice::LiverpoolGCDevice(PCIeBus* bus, const LiverpoolGCDeviceConfig& config)
: PCIeDevice(bus, config),
// Engines
gmc(bus->space_mem()), ih(*this, gmc)
gmc(bus->space_mem()), ih(*this, gmc),
smu(gmc, ih),
sam(gmc, ih, smu)
{
// Define BARs
space_bar0 = new MemorySpace(this, 0x4000000, {
@ -83,8 +84,6 @@ void LiverpoolGCDevice::reset() {
header.header_type |= PCI_HEADER_TYPE_MULTI_FUNCTION;
mmio.fill(0);
sam_ix.fill(0);
sam_sab_ix.fill(0);
}
U64 LiverpoolGCDevice::bar0_read(U64 addr, U64 size) {
@ -128,7 +127,8 @@ U64 LiverpoolGCDevice::mmio_read(U64 addr, U64 size) {
value = gmc.mmio_read(index);
return value;
}
else if (GMC_MMIO_MC.contains(index)) {
else if (GMC_MMIO_VM.contains(index)
|| GMC_MMIO_MC.contains(index)) {
value = gmc.mmio_read(index);
return value;
}
@ -136,6 +136,14 @@ U64 LiverpoolGCDevice::mmio_read(U64 addr, U64 size) {
value = ih.mmio_read(index);
return value;
}
else if (SMU_MMIO.contains(index)) {
value = smu.mmio_read(index);
return value;
}
else if (SAM_MMIO.contains(index)) {
value = sam.mmio_read(index);
return value;
}
switch (index) {
// ACP
@ -149,25 +157,6 @@ U64 LiverpoolGCDevice::mmio_read(U64 addr, U64 size) {
value = 0xFFFFFFFF;
break;
// SMU
case mmSMC_IND_INDEX:
value = mmio[index];
break;
case mmSMC_IND_DATA:
switch (mmio[mmSMC_IND_INDEX]) {
case 0xC2100004:
value = 0x2 | 0x1;
break;
case 0xC0500090:
value = 0x1;
break;
case 0xC0500098:
value = 0x1;
break;
default:
value = 0x0;
}
break;
// GCA
case mmGRBM_GFX_INDEX:
@ -182,25 +171,6 @@ U64 LiverpoolGCDevice::mmio_read(U64 addr, U64 size) {
value = 0;
break;
case mmSAM_IX_DATA:
index_ix = mmio[mmSAM_IX_INDEX];
DPRINTF("mmSAM_IX_DATA_read { index: %X }", index_ix);
value = sam_ix[index_ix];
break;
case mmSAM_SAB_IX_DATA:
index_ix = mmio[mmSAM_SAB_IX_INDEX];
DPRINTF("mmSAM_SAB_IX_DATA_read { index: %X }", index_ix);
value = sam_sab_ix[index_ix];
break;
// Simple registers
case mmSAM_GPR_SCRATCH_0:
case mmSAM_GPR_SCRATCH_1:
case mmSAM_GPR_SCRATCH_2:
case mmSAM_GPR_SCRATCH_3:
value = mmio[index];
break;
default:
DPRINTF("index=0x%llX, size=0x%llX", index, size);
assert_always("Unimplemented");
@ -222,39 +192,26 @@ void LiverpoolGCDevice::mmio_write(U64 addr, U64 value, U64 size) {
else if (GMC_MMIO_VM.contains(index)) {
gmc.mmio_write(index, value);
}
else if (GMC_MMIO_MC.contains(index)) {
else if (GMC_MMIO_VM.contains(index)
|| GMC_MMIO_MC.contains(index)) {
gmc.mmio_write(index, value);
return;
}
else if (OSS_MMIO_IH.contains(index)) {
ih.mmio_write(index, value);
return;
}
else if (SMU_MMIO.contains(index)) {
smu.mmio_write(index, value);
return;
}
else if (SAM_MMIO.contains(index)) {
sam.mmio_write(index, value);
return;
}
// Indirect registers
switch (index) {
case mmSAM_IX_DATA:
switch (mmio[mmSAM_IX_INDEX]) {
case ixSAM_IH_CPU_AM32_INT:
update_sam(value);
break;
case ixSAM_IH_AM32_CPU_INT_ACK:
sam_ix[ixSAM_IH_CPU_AM32_INT_STATUS] = 0;
break;
default:
index_ix = mmio[mmSAM_IX_INDEX];
DPRINTF("mmSAM_IX_DATA_write { index: %X, value: %llX }", index_ix, value);
sam_ix[index_ix] = value;
}
return;
case mmSAM_SAB_IX_DATA:
switch (mmio[mmSAM_SAB_IX_INDEX]) {
default:
index_ix = mmio[mmSAM_SAB_IX_INDEX];
DPRINTF("mmSAM_SAB_IX_DATA_write { index: %X, value: %llX }", index_ix, value);
sam_sab_ix[index_ix] = value;
}
return;
case mmMM_DATA:
mmio_write(mmio[mmMM_INDEX], value, size);
return;
@ -268,10 +225,6 @@ void LiverpoolGCDevice::mmio_write(U64 addr, U64 value, U64 size) {
mmio[mmACP_SOFT_RESET] = (value << 16);
break;
// SMU
case mmSMC_IND_INDEX:
break;
// GCA
case mmGRBM_GFX_INDEX:
case mmRLC_PG_ALWAYS_ON_CU_MASK:
@ -358,23 +311,3 @@ void LiverpoolGCDevice::mmio_write(U64 addr, U64 value, U64 size) {
assert_always("Unimplemented");
}
}
void LiverpoolGCDevice::update_sam(U32 value) {
U64 query_addr;
U64 reply_addr;
assert(value == 1);
query_addr = sam_ix[ixSAM_IH_CPU_AM32_INT_CTX_HIGH];
query_addr = sam_ix[ixSAM_IH_CPU_AM32_INT_CTX_LOW] | (query_addr << 32);
query_addr &= 0xFFFFFFFFFFFFULL;
reply_addr = sam_ix[ixSAM_IH_AM32_CPU_INT_CTX_HIGH];
reply_addr = sam_ix[ixSAM_IH_AM32_CPU_INT_CTX_LOW] | (reply_addr << 32);
reply_addr &= 0xFFFFFFFFFFFFULL;
const U16 flags = query_addr >> 48;
DPRINTF("flags=0x%llX, query=0x%llX, reply=0x%llX\n", flags, query_addr, reply_addr);
sam_ix[ixSAM_IH_CPU_AM32_INT_STATUS] = 0;// 1;
sam_ix[ixSAM_IH_AM32_CPU_INT_STATUS] |= 1;
}

View file

@ -15,6 +15,7 @@
// Engines
#include "gmc/gmc.h"
#include "oss/ih.h"
#include "smu/smu.h"
#include <array>
#include <memory>
@ -66,16 +67,9 @@ private:
U64 mmio_read(U64 addr, U64 size);
void mmio_write(U64 addr, U64 value, U64 size);
// GMC
// Engines
GmcDevice gmc;
// OSS
IhDevice ih;
// SAMU
std::unique_ptr<SAMUDevice> sam;
std::array<U32, 0x80> sam_ix;
std::array<U32, 0x40> sam_sab_ix;
void update_sam(U32 value);
SmuDevice smu;
SamDevice sam;
};

View file

@ -0,0 +1,206 @@
/**
* AMD Secure Asset Management Unit (SAMU) device.
*
* Copyright 2017-2021. Orbital project.
* Released under MIT license. Read LICENSE for more details.
*
* Authors:
* - Alexandro Sanchez Bach <alexandro@phi.nz>
*/
#include "sam.h"
#include <orbital/hardware/liverpool/gmc/gmc.h>
#include <orbital/hardware/liverpool/oss/ih.h>
#include <orbital/hardware/liverpool/smu/smu.h>
// SBL
constexpr U32 SBL_INT_UNKA202 = 0xA202;
constexpr U32 SBL_INT_UNKA303 = 0xA303;
constexpr U32 SBL_INT_SMU_READ = 0xA404;
constexpr U32 SBL_INT_SMU_WRITE = 0xA505;
// Logging
#define DEBUG_SAM 1
#define DPRINTF(...) \
do { \
if (DEBUG_SAM) { \
fprintf(stderr, "lvp-sam (%s:%d): ", __FUNCTION__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (0)
SamDevice::SamDevice(GmcDevice& gmc, IhDevice& ih, SmuDevice& smu)
: Device(nullptr), gmc(gmc), ih(ih), smu(smu) {
reset();
}
void SamDevice::reset() {
gpr.fill(0);
ih_cpu_am32_int_ctx = 0;
ih_am32_cpu_int_ctx = 0;
ix_data.fill(0);
sab_ix_data.fill(0);
}
U32 SamDevice::mmio_read(U32 index) {
U32 value = 0;
switch (index) {
case mmSAM_IX_INDEX:
value = sam_ix_index;
break;
case mmSAM_IX_DATA:
switch (sam_ix_index) {
case ixSAM_IH_CPU_AM32_INT_CTX_HIGH:
value = ih_cpu_am32_int_ctx_high;
break;
case ixSAM_IH_CPU_AM32_INT_CTX_LOW:
value = ih_cpu_am32_int_ctx_low;
break;
case ixSAM_IH_AM32_CPU_INT_CTX_HIGH:
value = ih_am32_cpu_int_ctx_high;
break;
case ixSAM_IH_AM32_CPU_INT_CTX_LOW:
value = ih_am32_cpu_int_ctx_low;
break;
default:
DPRINTF("mmSAM_IX_DATA_read { index: %X }", sam_ix_index);
value = ix_data[sam_ix_index];
}
break;
case mmSAM_SAB_IX_INDEX:
value = sam_sab_ix_index;
break;
case mmSAM_SAB_IX_DATA:
DPRINTF("mmSAM_SAB_IX_DATA_read { index: %X }", sam_sab_ix_index);
value = sab_ix_data[sam_sab_ix_index];
break;
case mmSAM_GPR_SCRATCH_0:
value = gpr[0];
break;
case mmSAM_GPR_SCRATCH_1:
value = gpr[1];
break;
case mmSAM_GPR_SCRATCH_2:
value = gpr[2];
break;
case mmSAM_GPR_SCRATCH_3:
value = gpr[3];
break;
default:
assert_always("Unimplemented");
}
return value;
}
void SamDevice::mmio_write(U32 index, U32 value) {
switch (index) {
case mmSAM_IX_INDEX:
sam_ix_index = value;
break;
case mmSAM_IX_DATA:
switch (sam_ix_index) {
case ixSAM_IH_CPU_AM32_INT:
handle_request(value);
break;
case ixSAM_IH_CPU_AM32_INT_CTX_HIGH:
ih_cpu_am32_int_ctx_high = value;
break;
case ixSAM_IH_CPU_AM32_INT_CTX_LOW:
ih_cpu_am32_int_ctx_low = value;
break;
case ixSAM_IH_AM32_CPU_INT_CTX_HIGH:
ih_am32_cpu_int_ctx_high = value;
break;
case ixSAM_IH_AM32_CPU_INT_CTX_LOW:
ih_am32_cpu_int_ctx_low = value;
break;
case ixSAM_IH_AM32_CPU_INT_ACK:
//sam_ix[ixSAM_IH_AM32_CPU_INT_STATUS] = 0;
ix_data[ixSAM_IH_CPU_AM32_INT_STATUS] = 0;
break;
default:
DPRINTF("mmSAM_IX_DATA_write { index: %X, value: %llX }", sam_ix_index, value);
ix_data[sam_ix_index] = value;
}
break;
case mmSAM_SAB_IX_INDEX:
sam_sab_ix_index = value;
break;
case mmSAM_SAB_IX_DATA:
switch (sam_sab_ix_index) {
default:
DPRINTF("mmSAM_SAB_IX_DATA_write { index: %X, value: %llX }", sam_sab_ix_index, value);
ix_data[sam_sab_ix_index] = value;
}
break;
case mmSAM_GPR_SCRATCH_0:
gpr[0] = value;
break;
case mmSAM_GPR_SCRATCH_1:
gpr[1] = value;
break;
case mmSAM_GPR_SCRATCH_2:
gpr[2] = value;
break;
case mmSAM_GPR_SCRATCH_3:
gpr[3] = value;
break;
default:
assert_always("Unimplemented");
}
}
void SamDevice::handle_request(U32 value) {
const auto query_addr = ih_cpu_am32_int_ctx & UINT64_C(0xFFFFFFFFFFFF);
const auto reply_addr = ih_am32_cpu_int_ctx & UINT64_C(0xFFFFFFFFFFFF);
assert(value == 1);
const U16 flags = ih_cpu_am32_int_flags;
DPRINTF("flags=0x%llX, query=0x%llX, reply=0x%llX\n", flags, query_addr, reply_addr);
if (flags == 0) {
const auto id = gpr[0];
switch (id) {
case SBL_INT_UNKA202:
assert_always("Unimplemented");
break;
case SBL_INT_UNKA303:
assert_always("Unimplemented");
break;
case SBL_INT_SMU_READ:
gpr[2] = smu.smc_read(gpr[1]);
gpr[3] = 0;
break;
case SBL_INT_SMU_WRITE:
smu.smc_write(gpr[1], gpr[2]);
gpr[3] = 0;
break;
}
ix_data[ixSAM_IH_CPU_AM32_INT_STATUS] = 0;// 1;
ix_data[ixSAM_IH_AM32_CPU_INT_STATUS] |= 1;
return;
}
else {
#if 0
uint32_t command = ldl_le_phys(s->gart.as[15 /*gart->as[SAMU_VMID] */], query_addr);
if (command == 0) {
liverpool_gc_samu_init(&s->samu, query_addr);
}
else {
liverpool_gc_samu_packet(&s->samu, query_addr, reply_addr);
}
if (command == SAMU_CMD_SERVICE_RAND || command == 0) {
return;
}
#endif
ix_data[ixSAM_IH_CPU_AM32_INT_STATUS] = 0;//1
ix_data[ixSAM_IH_AM32_CPU_INT_STATUS] |= 1;
ih.push_iv(0, IV_SRCID_SAM, 0 /* TODO */);
}
}

View file

@ -12,6 +12,16 @@
#pragma once
#include <orbital/core.h>
#include <orbital/offset_range.h>
// Forward declarations
class GmcDevice;
class IhDevice;
class SmuDevice;
constexpr auto SAM_MMIO = OffsetRange(0x8800, 0x20);
// SAMU
#define mmSAM_IX_INDEX 0x00008800
#define mmSAM_IX_DATA 0x00008801
@ -31,6 +41,7 @@
#define ixSAM_IH_AM32_CPU_INT_CTX_HIGH 0x00000035
#define ixSAM_IH_AM32_CPU_INT_CTX_LOW 0x00000036
#define ixSAM_IH_AM32_CPU_INT_ACK 0x00000037
#define ixSAM_UNK3E 0x0000003E
#define ixSAM_IH_CPU_AM32_INT_STATUS 0x0000004A
#define ixSAM_IH_AM32_CPU_INT_STATUS 0x0000004B
#define ixSAM_RST_HOST_SOFT_RST_RDY 0x00000051
@ -126,6 +137,41 @@ struct samu_command_service_rand_t {
LE<U08> data[0x10];
};
class SAMUDevice : public Device {
class SamDevice : public Device {
public:
SamDevice(GmcDevice& gmc, IhDevice& ih, SmuDevice& smu);
void reset();
U32 mmio_read(U32 index);
void mmio_write(U32 index, U32 value);
private:
GmcDevice& gmc;
IhDevice& ih;
SmuDevice& smu;
std::array<U32, 4> gpr;
std::array<U32, 0x80> ix_data;
std::array<U32, 0x40> sab_ix_data;
U32 sam_ix_index;
U32 sam_sab_ix_index;
union {
LE<U64> ih_cpu_am32_int_ctx;
Bitfield<U64, 48, 16> ih_cpu_am32_int_flags;
struct {
LE<U32> ih_cpu_am32_int_ctx_low;
LE<U32> ih_cpu_am32_int_ctx_high;
};
};
union {
LE<U64> ih_am32_cpu_int_ctx;
struct {
LE<U32> ih_am32_cpu_int_ctx_low;
LE<U32> ih_am32_cpu_int_ctx_high;
};
};
void handle_request(U32 value);
};

View file

@ -0,0 +1,94 @@
/**
* AMD System Management Unit (SMU) device.
*
* Copyright 2017-2021. Orbital project.
* Released under MIT license. Read LICENSE for more details.
*
* Authors:
* - Alexandro Sanchez Bach <alexandro@phi.nz>
*/
#include "smu.h"
#include <orbital/hardware/liverpool/gmc/gmc.h>
#include <orbital/hardware/liverpool/oss/ih.h>
#include "smu_7_1_2_d.h"
#include "smu_7_1_2_sh_mask.h"
SmuDevice::SmuDevice(GmcDevice& gmc, IhDevice& ih)
: Device(nullptr), gmc(gmc), ih(ih) {
reset();
}
void SmuDevice::reset() {
}
U32 SmuDevice::mmio_read(U32 index) {
U32 value = 0;
switch (index) {
case mmSMC_IND_INDEX:
value = smc_ix;
break;
case mmSMC_IND_DATA:
value = smc_read(smc_ix);
break;
default:
assert_always("Unimplemented");
}
return value;
}
void SmuDevice::mmio_write(U32 index, U32 value) {
switch (index) {
case mmSMC_IND_INDEX:
smc_ix = value;
break;
case mmSMC_IND_DATA:
smc_write(smc_ix, value);
break;
default:
assert_always("Unimplemented");
}
}
U32 SmuDevice::smc_read(U32 index) {
U32 value = 0;
switch (index) {
case 0xC2100004:
value = 0x2 | 0x1;
break;
case 0xC0500090:
value = 0x1;
break;
case 0xC0500098:
value = 0x1;
break;
case ixCG_DCLK_STATUS:
value = 0x1;
break;
case ixCG_VCLK_STATUS:
value = 0x1;
break;
case ixCG_ECLK_STATUS:
value = 0x1;
break;
case 0xC05000E0:
value = 0x1;
break;
case 0xC05000E8:
value = 0x1;
break;
default:
//assert_always("Unimplemented");
break;
}
return value;
}
void SmuDevice::smc_write(U32 index, U32 value) {
//assert_always("Unimplemented");
}

View file

@ -0,0 +1,39 @@
/**
* AMD System Management Unit (SMU) device.
*
* Copyright 2017-2021. Orbital project.
* Released under MIT license. Read LICENSE for more details.
*
* Authors:
* - Alexandro Sanchez Bach <alexandro@phi.nz>
*/
#pragma once
#include <orbital/core.h>
#include <orbital/offset_range.h>
// Forward declarations
class GmcDevice;
class IhDevice;
constexpr auto SMU_MMIO = OffsetRange(0x80, 0x40);
class SmuDevice : public Device {
public:
SmuDevice(GmcDevice& gmc, IhDevice& ih);
void reset();
U32 mmio_read(U32 index);
void mmio_write(U32 index, U32 value);
U32 smc_read(U32 index);
void smc_write(U32 index, U32 value);
private:
GmcDevice& gmc;
IhDevice& ih;
U32 smc_ix;
};