Skip to content

Latest commit

 

History

History
663 lines (540 loc) · 17.6 KB

File metadata and controls

663 lines (540 loc) · 17.6 KB

系统启动

RISCV64

概述

RISC-V64 架构的多核唤醒机制基于 OpenSBI (Supervisor Binary Interface) 标准,通过 HSM (Hart State Management) 扩展来管理硬件线程(Hart)的启动和状态。RISC-V 提供了简单统一的多核启动接口。

关键组件

1. OpenSBI HSM 扩展

  • HSM (Hart State Management): RISC-V标准的硬件线程管理接口
  • SBI调用: 通过ecall指令与Machine模式的OpenSBI固件通信
  • Hart状态管理: 包括STARTED、STOPPED、START_PENDING等状态

2. 核心数据结构

Hart状态枚举 (3rd/opensbi_interface/src/include/opensbi_interface.h):

enum {
  HSM_HART_STATE_STARTED = 0,
  HSM_HART_STATE_STOPPED = 1,
  HSM_HART_STATE_START_PENDING = 2,
  HSM_HART_STATE_STOP_PENDING = 3,
  HSM_HART_STATE_SUSPENDED = 4,
  HSM_HART_STATE_SUSPEND_PENDING = 5,
  HSM_HART_STATE_RESUME_PENDING = 6,
};

SBI返回值结构:

struct sbiret {
  long error;   // 错误码
  long value;   // 返回值
};

多核启动流程

1. BSP (Bootstrap Processor) 初始化

src/arch/riscv64/arch_main.cppArchInit() 函数中:

void ArchInit(int argc, const char **argv) {
  // 1. 解析设备树获取基本信息
  KernelFdtSingleton::create(reinterpret_cast<uint64_t>(argv));

  // 2. 初始化基本信息
  BasicInfoSingleton::create(argc, argv);

  // 3. 解析内核ELF信息
  KernelElfSingleton::create(BasicInfoSingleton::instance().elf_addr);

  // 4. 通过SBI唤醒所有Hart
  for (size_t i = 0; i < BasicInfoSingleton::instance().core_count; i++) {
    auto ret = sbi_hart_start(i, reinterpret_cast<uint64_t>(_boot), 0);
    if ((ret.error != SBI_SUCCESS) &&
        (ret.error != SBI_ERR_ALREADY_AVAILABLE)) {
      klog::Warn("hart %d start failed: %d\n", i, ret.error);
    }
  }
}

2. Hart 启动代码

位于 src/arch/riscv64/boot.S,实现简洁的64位启动序列:

.section .text.boot
.global _boot
_boot:
    // 检查设备树地址是否有效
    beqz a1, 2f

    // 初始化全局指针寄存器 (可选)
    #if USE_NO_RELAX == 0
.option push
.option norelax
1:  auipc gp, %pcrel_hi(__global_pointer$)
    addi  gp, gp, %pcrel_lo(1b)
.option pop
    #endif

2:  // 根据Hart ID设置独立栈
    add t0, a0, 1        // t0 = hart_id + 1
    slli t0, t0, 12      // t0 *= 4096 (4KB per hart)
    la sp, stack_top     // 加载栈顶基址
    add sp, sp, t0       // sp = stack_top + hart_id * 4KB

    // 保存Hart ID到tp寄存器 (用于获取当前核心ID)
    mv tp, a0

    // 保存SBI传递的参数到栈
    addi sp, sp, -8*2    // 开辟栈空间
    sd a0, 0(sp)         // 保存Hart ID
    sd a1, 8(sp)         // 保存设备树地址

    // 跳转到C代码入口
    call _start

    // 如果返回则进入等待状态
    wfi

3. SBI Hart启动接口

sbi_hart_start() 函数实现 (3rd/opensbi_interface/src/opensbi_interface.c):

struct sbiret sbi_hart_start(unsigned long hartid, unsigned long start_addr,
                             unsigned long opaque) {
  return ecall(hartid, start_addr, opaque, 0, 0, 0,
               SBI_EXT_HSM_HART_START, SBI_EXT_HSM);
}

4. Hart入口处理

Hart启动后通过 src/main.cpp_start() 函数进入:

void _start(int argc, const char **argv) {
  if (argv != nullptr) {
    // BSP路径 (设备树地址有效)
    CppInit();
    main(argc, argv);
    CppDeInit();
  } else {
    // AP路径 (设备树地址为空)
    main_smp(argc, argv);
  }

  // 进入死循环
  while (true) { ; }
}

AP执行 main_smp() -> ArchInitSMP() (当前为空实现):

void ArchInitSMP(int, const char **) {}

核心ID获取机制

Hart ID管理

RISC-V使用 tp (Thread Pointer) 寄存器存储当前Hart ID:

// 在boot.S中设置
mv tp, a0  // 将Hart ID保存到tp寄存器
// 在C代码中获取
static __always_inline auto GetCurrentCoreId() -> size_t {
  return Tp::Read();
}

Tp寄存器访问

通过内联汇编实现:

// 读取tp寄存器
static __always_inline auto Read() -> typename RegInfo::DataType {
  typename RegInfo::DataType value{};
  __asm__ volatile("mv %0, tp" : "=r"(value) : :);
  return value;
}

// 写入tp寄存器
static __always_inline void Write(typename RegInfo::DataType value) {
  __asm__ volatile("mv tp, %0" : : "r"(value) :);
}

设备树信息获取

CPU核心数量获取

通过解析设备树获取系统CPU核心数:

[[nodiscard]] auto GetCoreCount() const -> size_t {
  size_t core_count = 0;
  auto offset = -1;

  while (true) {
    offset = fdt_next_node(fdt_header_, offset, nullptr);
    if (offset < 0) break;

    const auto *prop = fdt_get_property(fdt_header_, offset, "device_type", nullptr);
    if (prop != nullptr) {
      const char *device_type = reinterpret_cast<const char *>(prop->data);
      if (strcmp(device_type, "cpu") == 0) {
        ++core_count;
      }
    }
  }

  return core_count;
}

基本信息初始化

BasicInfo::BasicInfo(int, const char **argv) {
  // 从设备树获取内存信息
  auto [memory_base, memory_size] =
      KernelFdtSingleton::instance().GetMemory();
  physical_memory_addr = memory_base;
  physical_memory_size = memory_size;

  // 内核地址和大小
  kernel_addr = reinterpret_cast<uint64_t>(__executable_start);
  kernel_size = reinterpret_cast<uint64_t>(end) -
                reinterpret_cast<uint64_t>(__executable_start);

  // 设备树地址
  fdt_addr = reinterpret_cast<uint64_t>(argv);

  // CPU核心数
  core_count = KernelFdtSingleton::instance().GetCoreCount();
}

内存布局

栈分配

每个Hart分配4KB独立栈空间:

// Hart栈地址计算: stack_top + (hart_id + 1) * 4KB
add t0, a0, 1        // hart_id + 1
slli t0, t0, 12      // * 4096
la sp, stack_top     // 栈基址
add sp, sp, t0       // 最终栈指针

.section .bss.boot
.align 16
.global stack_top
stack_top:
    .space 4096 * 4  // 总共16KB栈空间

SBI接口详解

Hart状态管理接口

// 启动Hart
struct sbiret sbi_hart_start(unsigned long hartid,
                             unsigned long start_addr,
                             unsigned long opaque);

// 停止Hart
struct sbiret sbi_hart_stop(void);

// 获取Hart状态
struct sbiret sbi_hart_get_status(unsigned long hartid);

// Hart挂起
struct sbiret sbi_hart_suspend(uint32_t suspend_type,
                               unsigned long resume_addr,
                               unsigned long opaque);

错误码定义

enum {
  SBI_SUCCESS = 0,
  SBI_ERR_FAILED = -1,
  SBI_ERR_NOT_SUPPORTED = -2,
  SBI_ERR_INVALID_PARAM = -3,
  SBI_ERR_DENIED = -4,
  SBI_ERR_INVALID_ADDRESS = -5,
  SBI_ERR_ALREADY_AVAILABLE = -6,  // Hart已经启动
  SBI_ERR_ALREADY_STARTED = -7,
  SBI_ERR_ALREADY_STOPPED = -8,
  SBI_ERR_NO_SHMEM = -9,
};

特性和限制

支持特性

  • 基于标准SBI HSM扩展的多核启动
  • 简洁的64位启动序列,无需模式切换
  • 通过设备树动态获取CPU核心数
  • 基于tp寄存器的高效核心ID管理
  • 完整的Hart状态管理支持
  • 使用OpenSBI固件接口

当前限制

  • ArchInitSMP()函数为空实现,缺少AP特定初始化
  • 缺少Hart间通信机制(IPI)
  • 固定的栈大小分配(4KB per Hart)
  • 简化的错误处理机制
  • 依赖外部OpenSBI固件支持

调试和监控

系统提供Hart状态查询功能:

auto status = sbi_hart_get_status(hart_id);
if (status.error == SBI_SUCCESS) {
  klog::Info("Hart %d status: %s\n", hart_id,
             HSM_HART_STATES_NAME[status.value]);
}

支持的状态包括:

  • STARTED: Hart正在运行
  • STOPPED: Hart已停止
  • START_PENDING: Hart启动中
  • STOP_PENDING: Hart停止中
  • SUSPENDED: Hart已挂起
  • SUSPEND_PENDING: Hart挂起中
  • RESUME_PENDING: Hart恢复中

AARCH64

概述

AArch64 架构的多核唤醒机制基于 PSCI (Power State Coordination Interface) 标准,通过 SMC (Secure Monitor Call) 指令与 EL3 固件通信来管理 CPU 核心的电源状态。PSCI 提供了标准化的多核管理接口,简化了操作系统的多核启动流程。

关键组件

1. PSCI (Power State Coordination Interface)

  • 标准化接口: ARM 标准的电源管理和多核控制接口
  • SMC调用: 通过 Secure Monitor Call 与 EL3 固件通信
  • 电源状态管理: 支持 CPU ON/OFF、SUSPEND/RESUME 等状态转换

2. 核心数据结构

PSCI错误码 (3rd/cpu_io/include/aarch64/cpu.hpp):

enum ErrorCode {
  SUCCESS = 0,
  NOT_SUPPORTED = -1,
  INVALID_PARAMETERS = -2,
  DENIED = -3,
  ALREADY_ON = -4,          // CPU已经启动
  ON_PENDING = -5,          // CPU启动中
  INTERNAL_FAILURE = -6,
  NOT_PRESENT = -7,
  DISABLED = -8,
  INVALID_ADDRESS = -9,
};

SMC返回值结构:

struct SMCReturnValue {
  uint64_t a0;  // 返回码/状态
  uint64_t a1;  // 附加返回值
  uint64_t a2;  // 附加返回值
  uint64_t a3;  // 附加返回值
};

MPIDR_EL1寄存器字段 (多处理器亲和性寄存器):

struct MPIDR_EL1Info {
  struct Aff3 { /* 位[39:32] 亲和性级别3 */ };
  struct Aff2 { /* 位[23:16] 亲和性级别2 */ };
  struct Aff1 { /* 位[15:8]  亲和性级别1 */ };
  struct Aff0 { /* 位[7:0]   亲和性级别0 */ };
  struct MT   { /* 位[24]    多线程标志 */ };
  struct U    { /* 位[30]    单/多处理器标志 */ };
};

多核启动流程

1. BSP (Bootstrap Processor) 初始化

src/arch/aarch64/arch_main.cppArchInit() 函数中:

void ArchInit(int argc, const char **argv) {
  // 1. 解析设备树获取系统信息
  KernelFdtSingleton::create(strtoull(argv[2], nullptr, 16));

  // 2. 初始化串口
  auto [serial_base, serial_size, irq] =
      KernelFdtSingleton::instance().GetSerial();
  Pl011Singleton::create(serial_base);

  // 3. 初始化基本信息
  BasicInfoSingleton::create(argc, argv);

  // 4. 解析内核ELF信息
  KernelElfSingleton::create(BasicInfoSingleton::instance().elf_addr);

  // 5. 检查PSCI支持
  KernelFdtSingleton::instance().CheckPSCI();

  // 6. 启动所有CPU核心
  for (size_t i = 0; i < BasicInfoSingleton::instance().core_count; i++) {
    auto ret = cpu_io::psci::CpuOn(i, reinterpret_cast<uint64_t>(_boot), 0);
    if ((ret != cpu_io::psci::SUCCESS) && (ret != cpu_io::psci::ALREADY_ON)) {
      klog::Warn("cpu %d start failed: %d\n", i, ret);
    }
  }
}

2. CPU启动代码

位于 src/arch/aarch64/boot.S,实现简洁的AArch64启动序列:

.section .text.boot
.global _boot
_boot:
    // 获取CPU ID (从MPIDR_EL1寄存器)
    mrs x10, mpidr_el1
    and x10, x10, #0xFF     // 提取Aff0字段作为CPU ID

    // 根据CPU ID设置独立栈
    add x10, x10, #1        // x10 = cpu_id + 1
    lsl x10, x10, #12       // x10 *= 4096 (4KB per CPU)
    ldr x11, =stack_top     // 加载栈顶基址
    add x11, x11, x10       // 计算该CPU的栈顶
    mov sp, x11             // 设置栈指针

    // 保存传递的参数到栈
    stp x0, x1, [sp, #-16]! // 保存参数并调整栈指针

    // 跳转到C代码入口
    bl _start

    // 如果返回则进入死循环
    b .

3. PSCI CpuOn接口

cpu_io::psci::CpuOn() 函数实现:

static __always_inline auto CpuOn(uint64_t target_cpu,
                                  uint64_t entry_point_address,
                                  uint64_t context_id) -> enum ErrorCode {
  return (ErrorCode)SecureMonitorCall(kCPU_ON_64, target_cpu,
                                      entry_point_address, context_id,
                                      0, 0, 0, 0).a0;
}

其中kCPU_ON_64 = 0xC4000003是PSCI标准定义的64位CPU启动功能码。

4. SMC (Secure Monitor Call) 实现

static __always_inline auto SecureMonitorCall(uint64_t a0, uint64_t a1,
                                              uint64_t a2, uint64_t a3,
                                              uint64_t a4, uint64_t a5,
                                              uint64_t a6, uint64_t a7)
    -> const SMCReturnValue {
  SMCReturnValue result;
  register uint64_t x0 __asm__("x0") = a0;
  register uint64_t x1 __asm__("x1") = a1;
  register uint64_t x2 __asm__("x2") = a2;
  register uint64_t x3 __asm__("x3") = a3;
  register uint64_t x4 __asm__("x4") = a4;
  register uint64_t x5 __asm__("x5") = a5;
  register uint64_t x6 __asm__("x6") = a6;
  register uint64_t x7 __asm__("x7") = a7;

  __asm__ volatile("smc #0"
                   : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3)
                   : "r"(x4), "r"(x5), "r"(x6), "r"(x7)
                   : "memory");

  result.a0 = x0;  // 返回码
  result.a1 = x1;  // 附加返回值
  result.a2 = x2;
  result.a3 = x3;
  return result;
}

5. CPU入口处理

CPU启动后通过 src/main.cpp_start() 函数进入:

void _start(int argc, const char **argv) {
  if (argv != nullptr) {
    // BSP路径 (参数有效)
    CppInit();
    main(argc, argv);
    CppDeInit();
  } else {
    // AP路径 (参数为空)
    main_smp(argc, argv);
  }

  // 进入死循环
  while (true) { ; }
}

AP执行 main_smp() -> ArchInitSMP() (当前为空实现)

核心ID获取机制

MPIDR_EL1寄存器

AArch64使用MPIDR_EL1 (Multiprocessor Affinity Register) 获取CPU亲和性信息:

// 获取当前CPU ID
static __always_inline auto GetCurrentCoreId() -> size_t {
  return MPIDR_EL1::Aff0::Get();  // 返回亲和性级别0值
}

亲和性级别含义

  • Aff0 [7:0]: 核心级别 - 同一集群内的核心编号
  • Aff1 [15:8]: 集群级别 - 集群编号
  • Aff2 [23:16]: 更高级别亲和性
  • Aff3 [39:32]: 最高级别亲和性

寄存器访问实现

// 读取MPIDR_EL1寄存器
static __always_inline auto Read() -> typename RegInfo::DataType {
  typename RegInfo::DataType value{};
  __asm__ volatile("mrs %0, MPIDR_EL1" : "=r"(value) : :);
  return value;
}

// 提取Aff0字段
static __always_inline auto Get() -> typename RegInfo::DataType {
  return static_cast<typename RegInfo::DataType>(
      (Read() & RegInfo::kBitMask) >> RegInfo::kBitOffset);
}

PSCI设备树检查

CheckPSCI实现

验证设备树中的PSCI配置:

void CheckPSCI() const {
  // 1. 查找PSCI节点
  auto offset = fdt_path_offset(fdt_header_, "/psci");
  if (offset < 0) {
    klog::Err("Error finding /psci node: %s\n", fdt_strerror(offset));
    return;
  }

  // 2. 检查调用方法
  const auto *method_prop = fdt_get_property(fdt_header_, offset, "method", &len);
  const char *method_str = reinterpret_cast<const char *>(method_prop->data);
  klog::Debug("PSCI method: %s\n", method_str);

  // 当前只支持SMC方法
  if (strcmp(method_str, "smc") != 0) {
    klog::Err("Unsupported PSCI method: %s\n", method_str);
  }

  // 3. 验证功能ID
  assert_function_id("cpu_on", 0xC4000003);
  assert_function_id("cpu_off", 0x84000002);
  assert_function_id("cpu_suspend", 0xC4000001);
}

内存布局

栈分配

每个CPU分配4KB独立栈空间:

// CPU栈地址计算: stack_top + (cpu_id + 1) * 4KB
add x10, x10, #1        // cpu_id + 1
lsl x10, x10, #12       // * 4096
ldr x11, =stack_top     // 栈基址
add x11, x11, x10       // 最终栈指针

.section .bss.boot
.align 16
.global stack_top
stack_top:
    .space 4096 * 4     // 总共16KB栈空间

PSCI功能接口

核心电源管理

// CPU启动
auto CpuOn(uint64_t target_cpu, uint64_t entry_point_address,
           uint64_t context_id) -> enum ErrorCode;

// CPU关闭
auto CpuOff() -> enum ErrorCode;

// CPU挂起
auto CpuSuspend(PowerState power_state, uint64_t entry_point_address,
                uint64_t context_id) -> enum ErrorCode;

target_cpu参数格式

CPU标识符遵循MPIDR格式:

  • 位[39:32]: Aff3 - 匹配目标CPU的MPIDR Aff3
  • 位[23:16]: Aff2 - 匹配目标CPU的MPIDR Aff2
  • 位[15:8]: Aff1 - 匹配目标CPU的MPIDR Aff1
  • 位[7:0]: Aff0 - 匹配目标CPU的MPIDR Aff0
  • 位[63:40]位[31:24]: 必须为0

电源状态结构

struct PowerState {
  uint32_t reserved1 : 6;      // [31:26] 保留
  uint32_t power_level : 2;    // [25:24] 电源级别
  uint32_t reserved0 : 7;      // [23:17] 保留
  uint32_t state_type : 1;     // [16] 状态类型
  struct StateID state_id;     // [15:0] 状态ID
} __attribute__((packed));

特性和限制

支持特性

  • 基于ARM标准PSCI接口的多核启动
  • 通过SMC与EL3固件安全通信
  • 基于MPIDR_EL1的层次化CPU标识
  • 设备树驱动的动态配置
  • 完整的CPU电源状态管理

当前限制

  • ArchInitSMP()函数为空实现,缺少AP特定初始化
  • 只支持SMC调用方法,不支持HVC
  • 固定的栈大小分配(4KB per CPU)
  • 简化的错误处理机制

调试和监控

PSCI功能验证

// 检查PSCI版本
auto version = SecureMonitorCall(kVERSION, 0, 0, 0, 0, 0, 0, 0);
klog::Info("PSCI Version: %d.%d\n",
           (version.a0 >> 16) & 0xFFFF, version.a0 & 0xFFFF);

// 检查功能支持
auto features = SecureMonitorCall(kFEATURES, kCPU_ON_64, 0, 0, 0, 0, 0, 0);
if (features.a0 == SUCCESS) {
  klog::Info("PSCI CPU_ON_64 supported\n");
}

CPU状态查询

// 获取CPU亲和性信息
auto affinity_info = SecureMonitorCall(kAFFINITY_INFO_64, target_cpu,
                                      0, 0, 0, 0, 0, 0);
klog::Info("CPU %d affinity state: %d\n", target_cpu, affinity_info.a0);

错误处理

常见的PSCI错误码处理:

  • ALREADY_ON: CPU已经在运行,可以忽略
  • ON_PENDING: CPU正在启动过程中,需要等待
  • NOT_PRESENT: CPU不存在,检查设备树配置
  • INVALID_PARAMETERS: 参数错误,检查target_cpu格式
  • DENIED: 权限不足,检查EL3固件配置