Skip to content

A Previous Stage Bootloader Example

Shawn Xu edited this page Aug 13, 2024 · 1 revision

OpenSBI cannot work in XIP mode, so it need a previous stage bootloader to do something it cannot do.

Here is an example using hpm sdk.

CMakeLists.txt:

# Copyright (c) 2024 HPMicro
# SPDX-License-Identifier: BSD-3-Clause

cmake_minimum_required(VERSION 3.13)

find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})

project(opensbi_loader)

sdk_app_src(src/loader.c)
sdk_compile_definitions(-DINIT_EXT_RAM_FOR_DATA)
sdk_compile_definitions(-DUSE_NONVECTOR_MODE=1)
generate_ide_projects()

  • INIT_EXT_RAM_FOR_DATA needs to be defined to 1 to init the SDRAM
  • USE_NONVECTOR_MODE needs to be defined to 1 to avoid sdk using the vector mode which is not supported by OpenSBI

C source

/*
 * Copyright (c) 2021 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include <stdio.h>
#include "hpm_debug_console.h"
#include "hpm_pcfg_drv.h"
#include "hpm_clock_drv.h"
#include "hpm_uart_drv.h"
#include "hpm_pllctlv2_drv.h"
#include "pinmux.h"

#define LED_FLASH_PERIOD_IN_MS 300
#define OPENSBI_SRC_ADDR    0x80010000
#define OPENSBI_SIZE        0x18000
#define OPENSBI_EXEC_ADDR    0

#define LINUX_IMAGE_SRC_ADDR 0x80040000
#define LINUX_IMAGE_SIZE_MAX (2 * 1024 * 1024)
#define LINUX_IMAGE_EXEC_ADDR 0x40000000

#define LINUX_DTB_SRC_ADDR 0x80310000
#define LINUX_DTB_SIZE_MAX  0x4000
#define LINUX_DTB_DST_ADDR  0x40300000

void (*open_sbi_entry)(int, int, int);

static void loader_pre_init_clock(void)
{
    uint32_t cpu0_freq = clock_get_frequency(clock_cpu0);
    if (cpu0_freq == PLLCTL_SOC_PLL_REFCLK_FREQ) {
        /* Configure the External OSC ramp-up time: ~9ms */
        pllctlv2_xtal_set_rampup_time(HPM_PLLCTLV2, 32UL * 1000UL * 9U);

        /* Select clock setting preset1 */
        sysctl_clock_set_preset(HPM_SYSCTL, 2);
    }


    /* Add most Clocks to group 0 */
    /* not open uart clock in this API, uart should configure pin function before opening clock */
    clock_add_to_group(clock_cpu0, 0);
    clock_add_to_group(clock_ahbp, 0);
    clock_add_to_group(clock_axic, 0);
    clock_add_to_group(clock_axis, 0);

    clock_add_to_group(clock_mchtmr0, 0);
    clock_add_to_group(clock_femc, 0);
    clock_add_to_group(clock_xpi0, 0);
    clock_add_to_group(clock_xpi1, 0);
    clock_add_to_group(clock_sdp, 0);
    clock_add_to_group(clock_xdma, 0);
    clock_add_to_group(clock_ram0, 0);
    clock_add_to_group(clock_gpio, 0);
    clock_add_to_group(clock_mbx0, 0);
    clock_add_to_group(clock_hdma, 0);
    clock_add_to_group(clock_rng, 0);

    /* Connect Group0 to CPU0 */
    clock_connect_group_to_cpu(0, 0);

    /* Configure CPU to 480MHz, AXI/AHB to 160MHz */
    sysctl_config_cpu0_domain_clock(HPM_SYSCTL, clock_source_pll1_clk0, 1, 3, 3);
    /* Configure PLL1_CLK0 Post Divider to 1.2 */
    pllctlv2_set_postdiv(HPM_PLLCTLV2, 1, 0, 1);
    /* Configure PLL1 clock frequencey to 576MHz, the PLL1_CLK0 frequency = 576MHz / 1.2 = 480MHz */
    pllctlv2_init_pll_with_freq(HPM_PLLCTLV2, 1, 576000000);
    clock_update_core_clock();

    /* Configure mchtmr to 24MHz */
    clock_set_source_divider(clock_mchtmr0, clk_src_osc24m, 1);
}


static void loader_pre_init_console_clock(void)
{
    /* uart needs to configure pin function before enabling clock, otherwise the level change of
    uart rx pin when configuring pin function will cause a wrong data to be received.
    And a uart rx dma request will be generated by default uart fifo dma trigger level. */
    init_uart_pins((UART_Type *) HPM_UART0);

    /* Configure the UART clock to 24MHz */
    clock_set_source_divider(clock_uart0, clk_src_osc24m, 1U);
    clock_add_to_group(clock_uart0, 0);
}

int main(void)
{
    pcfg_dcdc_set_voltage(HPM_PCFG, 1100);

    loader_pre_init_clock();

    loader_pre_init_console_clock();

    memcpy((void *)OPENSBI_EXEC_ADDR, (void *)OPENSBI_SRC_ADDR, OPENSBI_SIZE);
    
    memcpy((void *)LINUX_IMAGE_EXEC_ADDR, (void *)LINUX_IMAGE_SRC_ADDR, LINUX_IMAGE_SIZE_MAX);
    
    memcpy((void *)LINUX_DTB_DST_ADDR, (void *)LINUX_DTB_SRC_ADDR, LINUX_DTB_SIZE_MAX);

    open_sbi_entry = (void(*)(int, int, int))OPENSBI_EXEC_ADDR;

    __asm volatile ("fence iorw, iorw");
    
    __asm volatile ("fence.i");

    open_sbi_entry(0, 0, 0);

    return 0;
}

Clone this wiki locally