内核线程能够调度

This commit is contained in:
zhangzheng
2024-04-01 16:10:59 +00:00
parent c57d9cbb82
commit c73c35c5b2
51 changed files with 900 additions and 349 deletions

View File

@@ -343,7 +343,9 @@
"slab.h": "c",
"arm_local_reg.h": "c",
"timer.h": "c",
"arm_gicv2.h": "c"
"arm_gicv2.h": "c",
"sched_arch.h": "c",
"thread_task_arch.h": "c"
},
"cortex-debug.showRTOS": false,
"cortex-debug.variableUseNaturalFormat": false,

View File

@@ -12,7 +12,7 @@ CONFIG_INIT_TASK_OFFSET=0x10000
CONFIG_BOOTFS_OFFSET=0x02000000
CONFIG_MK_MPU_CFG=n
CONFIG_FT_ADDR_NR=16
CONFIG_SYS_SCHE_HZ=1000
CONFIG_SYS_SCHE_HZ=100
CONFIG_USER_ISR_START_NO=16
CONFIG_IRQ_REG_TAB_SIZE=80
CONFIG_REGION_NUM=8
@@ -116,3 +116,5 @@ CONFIG_SMP=y
CONFIG_CPU=4
CONFIG_THREAD_BLOCK_SIZE=0x1000
CONFIG_PAGE_SHIFT=12
CONFIG_MMU=y
CONFIG_KNL_TEST=y

View File

@@ -4,7 +4,14 @@ menu "Knl config"
menuconfig KNL_INFO
bool "knl info set"
default y
config KNL_TEST
bool "enable knl test."
default n
if KNL_INFO
config MMU
bool "support mmu"
default n
config PAGE_SHIFT
int "page shift"
default 9

View File

@@ -30,5 +30,6 @@ target_include_directories(
${CMAKE_SOURCE_DIR}/mkrtos_knl/inc/lib
${CMAKE_SOURCE_DIR}/mkrtos_knl/inc/knl
${CMAKE_SOURCE_DIR}/mkrtos_knl/arch/${ARCH}/${CONFIG_CPU_TYPE}
)

View File

@@ -15,11 +15,11 @@
#include "config.h"
#include "thread.h"
#include "mk_sys.h"
#include "mpu.h"
#include <psci.h>
#include <arm_gicv2.h>
#include <timer/timer.h>
#include <hyp.h>
#include <sche_arch.h>
__ALIGN__(THREAD_BLOCK_SIZE)
static uint8_t thread_knl_stack[THREAD_BLOCK_SIZE] = {0};
void *_estack = thread_knl_stack + THREAD_BLOCK_SIZE;
@@ -29,8 +29,7 @@ void *_estack = thread_knl_stack + THREAD_BLOCK_SIZE;
*/
void to_sche(void)
{
// 开启pensv中断
/*TODO:*/
sche_arch_sw_context();
}
/**
* 进行一些系统的初始化

View File

@@ -16,6 +16,50 @@
#include <aarch64_ptregs.h>
#define LOG_INTR_NO 37 // USART1_IRQn
/// @brief 线程信息
typedef struct
{
umword_t rg0[8]; //!< r0-r3
umword_t r12;
umword_t lr;
umword_t pc;
} pf_s_t;
typedef struct pf
{
struct
{
mword_t regs[31]; //!< 基础寄存器
mword_t sp; //!< sp
mword_t pc; //!< pc
mword_t pstate; //!< pstate
};
mword_t orig_x0;
uint32_t syscallno;
uint32_t unused2;
mword_t orig_addr_limit;
mword_t unused;
mword_t stackframe[2];
} pf_t;
typedef struct sp_info
{
mword_t x19;
mword_t x20;
mword_t x21;
mword_t x22;
mword_t x23;
mword_t x24;
mword_t x25;
mword_t x26;
mword_t x27;
mword_t x28;
mword_t fp; // x29
mword_t sp;
mword_t pc;
} sp_info_t;
#define _barrier() __asm__ __volatile__("" : : : "memory")
#define _dmb(ins) \
asm volatile("dmb " #ins : : : "memory")
@@ -23,6 +67,15 @@
#define _dsb(ins) \
asm volatile("dsb " #ins : : : "memory")
#define __arch_getl(a) (*(volatile unsigned int *)(a))
#define __arch_putl(v, a) (*(volatile unsigned int *)(a) = (v))
#define __iormb() _barrier()
#define __iowmb() _barrier()
#define readl(c) ({ unsigned int __v = __arch_getl(c); __iormb(); __v; })
#define writel(v, c) ({ unsigned int __v = v; __iowmb(); __arch_putl(__v,c); })
#define read_reg(addr) (*((volatile umword_t *)(addr)))
#define write_reg(addr, data) \
do \
@@ -155,7 +208,7 @@ static inline umword_t intr_status(void)
umword_t ret;
asm volatile("mrs %0, daif" : "=r"(ret));
return !(ret & 0xc0);
return !!(ret & 0xc0);
}
void sys_startup(void);

View File

@@ -187,20 +187,9 @@ tsk .req x28 // current thread_info
el1_irq:
kernel_entry 2
bl entry_handler
get_thread_info tsk
// bl 2f
//1:
// kernel_exit 2
//2:
// mov x24, lr
//bl sched_printf
bl SysTick_Handler
kernel_exit 2
// ret x24
//
//string_test:
// .string "t"
.global trigger_alignment
trigger_alignment:
@@ -230,9 +219,6 @@ ret_form_run:
mov x0, x20
blr x19
1:
//ldr x19, =test_user_stack
//add x19, x19, #(4096 - 8)
//msr sp_el1, x19
b ret_to_user
/* prevnext

View File

@@ -2,29 +2,6 @@
#ifndef _P_IO_H
#define _P_BASE_H
#if 1
#define __arch_getl(a) (*(volatile unsigned int *)(a))
#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v))
#ifndef dmb
#define dmb() __asm__ __volatile__ ("" : : : "memory")
#endif
#define __iormb() dmb()
#define __iowmb() dmb()
#define readl(c) ({ unsigned int __v = __arch_getl(c); __iormb(); __v; })
#define writel(v,c) ({ unsigned int __v = v; __iowmb(); __arch_putl(__v,c);})
#else
static inline void writel(unsigned int value, unsigned int addr)
{
*(volatile unsigned int *)addr = value;
}
static inline unsigned int readl(unsigned int addr)
{
return *(volatile unsigned int *)addr;
}
#endif
static inline void delay(unsigned int n)
{

View File

@@ -3,28 +3,11 @@
/*
* PSR bits
*/
#define PSR_MODE_EL0t 0x00000000
#define PSR_MODE_EL1t 0x00000004
#define PSR_MODE_EL1h 0x00000005
#define PSR_MODE_EL2t 0x00000008
#define PSR_MODE_EL2h 0x00000009
#define PSR_MODE_EL3t 0x0000000c
#define PSR_MODE_EL3h 0x0000000d
#define PSR_MODE_MASK 0x0000000f
typedef struct entry_frame {
struct {
mword_t regs[31];
mword_t sp;
mword_t pc;
mword_t pstate;
};
mword_t orig_x0;
uint32_t syscallno;
uint32_t unused2;
mword_t orig_addr_limit;
mword_t unused;
mword_t stackframe[2];
} entry_frame_t;
#define PSR_MODE_EL0t 0x00000000
#define PSR_MODE_EL1t 0x00000004
#define PSR_MODE_EL1h 0x00000005
#define PSR_MODE_EL2t 0x00000008
#define PSR_MODE_EL2h 0x00000009
#define PSR_MODE_EL3t 0x0000000c
#define PSR_MODE_EL3h 0x0000000d
#define PSR_MODE_MASK 0x0000000f

View File

@@ -1,6 +1,5 @@
#include <arch.h>
#include "early_boot.h"
#include "asm/sysregs.h"
#include "asm/base.h"
#include "asm/mm.h"
@@ -10,7 +9,8 @@
#include <config.h>
#include <spinlock.h>
#include <assert.h>
#include <mm_page.h>
#include <early_boot.h>
#define DATA_BOOT_SECTION ".data.boot"
#define TEXT_BOOT_SECTION ".text.boot"

View File

@@ -4,16 +4,7 @@
#include "pager.h"
#include "asm/mm.h"
#include "asm_config.h"
#define PAGE_DEEP 4
typedef struct page_entry
{
pte_t *dir; //!< 存储页表地址
uint8_t lv_shift_sizes[PAGE_DEEP]; //!< 页表翻译的大小order
uint8_t depth; //!< 页表深度
} page_entry_t;
#include <mm_page.h>
pte_t *pages_walk(page_entry_t *pdir, addr_t virt_addr, mword_t size, void *(*fn_alloc)(void));
void map_mm(page_entry_t *pdir, addr_t virt_addr, addr_t phys_addr,
mword_t page_order, mword_t pfn_cn, mword_t attr);

View File

@@ -0,0 +1,15 @@
#include <buddy.h>
#include <asm/mm.h>
#include "mm_page.h"
#include <early_boot.h>
int page_entry_init(page_entry_t *entry)
{
entry->dir = buddy_alloc(buddy_get_alloter(), PAGE_SIZE);
if (entry->dir == NULL)
{
return -ENOMEM;
}
knl_pdir_init(entry, entry->dir, 3);
return 0;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <types.h>
#include "pager.h"
#include <err.h>
#define PAGE_DEEP 4
typedef struct page_entry
{
pte_t *dir; //!< 存储页表地址
uint8_t lv_shift_sizes[PAGE_DEEP]; //!< 页表翻译的大小order
uint8_t depth; //!< 页表深度
} page_entry_t;
int page_entry_init(page_entry_t *entry);

View File

@@ -0,0 +1,31 @@
/**
* @file mm_space.c
* @author ATShining (1358745329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "util.h"
#include "mm_space.h"
#include "assert.h"
#include <early_boot.h>
#include <mm_page.h>
void mm_space_init(mm_space_t *mm_space, int is_knl)
{
page_entry_init(&mm_space->mem_dir);
}
bool_t mm_space_add(mm_space_t *m_space,
umword_t addr,
umword_t size,
uint8_t attrs)
{
return 0;
}
void mm_space_del(mm_space_t *m_space, umword_t addr)
{
/*TODO:*/
}

View File

@@ -254,4 +254,3 @@ typedef struct pte
#define PTE_ADDR_LOW (((1UL << (48 - PAGE_SHIFT)) - 1) << PAGE_SHIFT)
#define PTE_ADDR_MASK PTE_ADDR_LOW
void paging_init(void);

View File

@@ -0,0 +1,35 @@
#include <asm_config.h>
#include <asm_offset.h>
/* prevnext
cpu_switch_to(struct cpu_context *prev,
struct cpu_context *next);
x19 ~ x29 sp lr
task_struct->cpu_context
*/
.align
.global cpu_switch_to
cpu_switch_to:
mov x8, x0
mov x9, sp
stp x19, x20, [x8], #16
stp x21, x22, [x8], #16
stp x23, x24, [x8], #16
stp x25, x26, [x8], #16
stp x27, x28, [x8], #16
stp x29, x9, [x8], #16
str lr, [x8]
mov x8, x1
ldp x19, x20, [x8], #16
ldp x21, x22, [x8], #16
ldp x23, x24, [x8], #16
ldp x25, x26, [x8], #16
ldp x27, x28, [x8], #16
ldp x29, x9, [x8], #16
ldr lr, [x8]
mov sp, x9
ret

View File

@@ -0,0 +1,55 @@
#include <types.h>
#include <thread.h>
#include <arch.h>
#include <util.h>
#include <sche_arch.h>
#include <mm_space.h>
#include <task.h>
static void sw_mmu(thread_t *next_thread)
{
umword_t p_curr_dir = read_sysreg(vttbr_el2);
task_t *next_task = (task_t *)(next_thread->task);
umword_t p_next_dir = (umword_t)mm_space_get_pdir(&next_task->mm_space)->dir;
if (p_curr_dir != p_next_dir)
{
_dsb(sy);
write_sysreg(p_next_dir | (1UL << 48) /*TODO:*/, vttbr_el2);
_dsb(ish);
_isb();
asm volatile("ic iallu");
// mword_t vttbr;
// // FIXME: could do a compare for the current VMID before loading
// // the vttbr and the isb
// asm volatile(
// "mrs %[vttbr], vttbr_el2\n"
// "msr vttbr_el2, %[asid] \n"
// "isb \n"
// "dsb ishst \n"
// "tlbi ipas2e1, %[ipa] \n"
// "dsb ish \n"
// "tlbi vmalle1 \n"
// "dsb ish \n"
// "msr vttbr_el2, %[vttbr]\n"
// :
// [vttbr] "=&r" (vttbr)
// :
// [ipa] "r" ((unsigned long)va >> 12),
// [asid] "r" (1 << 48)
// :
// "memory");
}
}
void sche_arch_sw_context(void)
{
scheduler_t *sche = scheduler_get_current();
thread_t *cur_th = thread_get_current();
sched_t *next = sche->cur_sche;
thread_t *next_th = container_of(next, thread_t, sche);
// 这里切换页表
sw_mmu(next_th);
// 变成了next的sp
cpu_switch_to(&cur_th->sp, &next_th->sp);
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include <arch.h>
void cpu_switch_to(sp_info_t *prev,
sp_info_t *next);
void sche_arch_sw_context(void);

View File

@@ -0,0 +1,46 @@
/**
* @file thread_armv7m.c
* @author ATShining (135874329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "thread.h"
#include "printk.h"
#include "app.h"
#include "mm_wrap.h"
#include "arch.h"
#include "string.h"
#include <asm/system.h>
#include <early_boot.h>
#include <task.h>
extern void ret_form_run(void);
syscall_entry_func syscall_handler_get(void)
{
return syscall_entry;
}
void thread_knl_pf_set(thread_t *cur_th, void *pc)
{
pf_t *pt = ((pf_t *)((char *)cur_th + THREAD_BLOCK_SIZE)) - 1;
pt->pstate = PSR_MODE_EL2h;
// pt->pc = (umword_t)pc;
cur_th->sp.x19 = (umword_t)pc;
cur_th->sp.x20 = 0 /*arg*/;
cur_th->sp.pc = (mword_t)ret_form_run;
cur_th->sp.sp = (umword_t)pt;
}
void thread_user_pf_set(thread_t *cur_th, void *pc, void *user_sp, void *ram, umword_t stack)
{
/*TODO:*/
assert(0);
}
void task_knl_init(task_t *knl_tk)
{
knl_tk->mm_space.mem_dir = *boot_get_pdir();
}

View File

@@ -0,0 +1,25 @@
#include <scheduler.h>
#include <types.h>
#include <thread.h>
#include <util.h>
sp_info_t *schde_to(void *usp, void *ksp, umword_t sp_type)
{
scheduler_t *sche = scheduler_get_current();
sched_t *next = sche->cur_sche;
thread_t *next_th = container_of(next, thread_t, sche);
assert(next_th->magic == THREAD_MAGIC);
if (sched_reset)
{
thread_t *cur_th = thread_get_current();
cur_th->sp.knl_sp = ksp;
cur_th->sp.user_sp = usp;
cur_th->sp.sp_type = sp_type;
}
sched_reset = 1;
return &next_th->sp;
}

View File

@@ -14,6 +14,28 @@
#include "arch.h"
#define LOG_INTR_NO 38 // USART2_IRQn
/// @brief 线程信息
typedef struct
{
umword_t rg0[4]; //!< r0-r3
umword_t r12;
umword_t lr;
umword_t pc;
umword_t xpsr;
} pf_s_t;
typedef struct pf
{
umword_t rg1[8]; //!< r4-r11
pf_s_t pf_s;
} pf_t;
typedef struct sp_info
{
void *user_sp; //!< 用户态的sp
void *knl_sp; //!< 内核sp
mword_t sp_type; //!< 使用的栈类型
} sp_info_t;
#define read_reg(addr) (*((volatile umword_t *)(addr)))
#define write_reg(addr, data) \
do \
@@ -88,17 +110,17 @@ void arch_disable_irq(int inx);
void arch_enable_irq(int inx);
void arch_set_enable_irq_prio(int inx, int sub_prio, int pre_prio);
#define sti() \
#define sti() \
do \
{ \
write_sysreg(0, PRIMASK); \
} while (0)
#define cli() \
do \
{ \
__asm__ __volatile__("CPSID I\n" :: \
:); \
} while (0)
#define cli() \
do \
{ \
write_sysreg(0, PRIMASK); \
} while (0)
static inline __attribute__((optimize(0))) void preemption(void)
{
@@ -118,4 +140,3 @@ void sys_reset(void);
umword_t sys_tick_cnt_get(void);
uint32_t arch_get_sys_clk(void);

View File

@@ -12,7 +12,29 @@
#include "types.h"
#include "arch.h"
#define LOG_INTR_NO 37//USART1_IRQn
#define LOG_INTR_NO 37 // USART1_IRQn
/// @brief 线程信息
typedef struct
{
umword_t rg0[4]; //!< r0-r3
umword_t r12;
umword_t lr;
umword_t pc;
umword_t xpsr;
} pf_s_t;
typedef struct pf
{
umword_t rg1[8]; //!< r4-r11
pf_s_t pf_s;
} pf_t;
typedef struct sp_info
{
void *user_sp; //!< 用户态的sp
void *knl_sp; //!< 内核sp
mword_t sp_type; //!< 使用的栈类型
} sp_info_t;
#define read_reg(addr) (*((volatile umword_t *)(addr)))
#define write_reg(addr, data) \
@@ -43,7 +65,7 @@
: "=r"(ret) \
: \
:); \
((ret & 0x4) ? 0 : 1); \
((ret & 0x4) ? 0 : 1); \
})
void to_sche(void);
@@ -88,17 +110,17 @@ void arch_disable_irq(int inx);
void arch_enable_irq(int inx);
void arch_set_enable_irq_prio(int inx, int sub_prio, int pre_prio);
#define sti() \
#define sti() \
do \
{ \
write_sysreg(0, PRIMASK); \
} while (0)
#define cli() \
do \
{ \
__asm__ __volatile__("CPSID I\n" :: \
:); \
} while (0)
#define cli() \
do \
{ \
write_sysreg(0, PRIMASK); \
} while (0)
static inline __attribute__((optimize(0))) void preemption(void)
{

View File

@@ -0,0 +1,85 @@
/**
* @file mm_space.c
* @author ATShining (1358745329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "util.h"
#include "mm_space.h"
#include "mpu.h"
#include "assert.h"
#if CONFIG_MK_MPU_CFG
void mm_space_init(mm_space_t *mm_space, int is_knl)
{
for (int i = 0; i < CONFIG_REGION_NUM; i++)
{
mm_space->pt_regions[i].region_inx = -1;
}
}
region_info_t *mm_space_alloc_pt_region(mm_space_t *m_space)
{
for (int i = 0; i < CONFIG_REGION_NUM; i++)
{
/*TODO:原子操作*/
if (m_space->pt_regions[i].region_inx < 0)
{
m_space->pt_regions[i].region_inx = (int16_t)i;
return &m_space->pt_regions[i];
}
}
return NULL;
}
void mm_space_free_pt_region(mm_space_t *m_space, region_info_t *ri)
{
ri->region_inx = -1;
}
bool_t mm_space_add(mm_space_t *m_space,
umword_t addr,
umword_t size,
uint8_t attrs)
{
region_info_t *ri = mm_space_alloc_pt_region(m_space);
if (!ri)
{
return FALSE;
}
#if CONFIG_MPU_VERSION == 1
if (!is_power_of_2(size) || (addr & (!(size - 1))) != 0)
{
//!< 申请的大小必须是2的整数倍而且地址也必须是2的整数倍
mm_space_free_pt_region(m_space, ri);
return FALSE;
}
#elif CONFIG_MPU_VERSION == 2
if ((size & (MPU_ALIGN_SIZE - 1)) == 0 && (addr & (MPU_ALIGN_SIZE - 1)) == 0)
{
mm_space_free_pt_region(m_space, ri);
return FALSE;
}
#endif
mpu_calc_regs(ri, addr, size, attrs, 0);
ri->start_addr = addr;
ri->size = size;
return TRUE;
}
void mm_space_del(mm_space_t *m_space, umword_t addr)
{
for (int i = 0; i < CONFIG_REGION_NUM; i++)
{
if (m_space->pt_regions[i].region_inx >= 0 &&
m_space->pt_regions[i].start_addr == addr)
{
m_space->pt_regions[i].region_inx = -1;
m_space->pt_regions[i].start_addr = 0;
break;
}
}
}
#endif

View File

@@ -0,0 +1,25 @@
#include <scheduler.h>
#include <types.h>
#include <thread.h>
#include <util.h>
sp_info_t *schde_to(void *usp, void *ksp, umword_t sp_type)
{
scheduler_t *sche = scheduler_get_current();
sched_t *next = sche->cur_sche;
thread_t *next_th = container_of(next, thread_t, sche);
assert(next_th->magic == THREAD_MAGIC);
if (sched_reset)
{
thread_t *cur_th = thread_get_current();
cur_th->sp.knl_sp = ksp;
cur_th->sp.user_sp = usp;
cur_th->sp.sp_type = sp_type;
}
sched_reset = 1;
return &next_th->sp;
}

View File

@@ -15,6 +15,28 @@
#define LOG_INTR_NO 0 // UART0_IRQn
/// @brief 线程信息
typedef struct
{
umword_t rg0[4]; //!< r0-r3
umword_t r12;
umword_t lr;
umword_t pc;
umword_t xpsr;
} pf_s_t;
typedef struct pf
{
umword_t rg1[8]; //!< r4-r11
pf_s_t pf_s;
} pf_t;
typedef struct sp_info
{
void *user_sp; //!< 用户态的sp
void *knl_sp; //!< 内核sp
mword_t sp_type; //!< 使用的栈类型
} sp_info_t;
#define read_reg(addr) (*((volatile umword_t *)(addr)))
#define write_reg(addr, data) \
do \
@@ -92,12 +114,12 @@ void arch_set_enable_irq_prio(int inx, int sub_prio, int pre_prio);
#define sti() \
do \
{ \
write_sysreg(1, PRIMASK); \
write_sysreg(0, PRIMASK); \
} while (0)
#define cli() \
do \
{ \
write_sysreg(0, PRIMASK); \
write_sysreg(1, PRIMASK); \
} while (0)
static inline __attribute__((optimize(0))) void preemption(void)

View File

@@ -0,0 +1,55 @@
/**
* @file thread_armv7m.c
* @author ATShining (135874329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "thread.h"
#include "printk.h"
#include "thread_armv7m.h"
#include "app.h"
#include "mm_wrap.h"
#include "arch.h"
#include "string.h"
syscall_entry_func syscall_handler_get(void)
{
return syscall_entry;
}
void thread_knl_pf_set(thread_t *cur_th, void *pc)
{
pf_t *cur_pf = ((pf_t *)((char *)cur_th + THREAD_BLOCK_SIZE)) - 1;
cur_pf->pf_s.xpsr = 0x01000000L;
cur_pf->pf_s.lr = (umword_t)NULL; //!< 线程退出时调用的函数
cur_pf->pf_s.pc = (umword_t)pc | 0x1;
// cur_pf->rg1[5] = (umword_t)0;
cur_th->sp.knl_sp = (char *)cur_pf;
cur_th->sp.user_sp = 0;
cur_th->sp.sp_type = 0xfffffff9;
}
void thread_user_pf_set(thread_t *cur_th, void *pc, void *user_sp, void *ram, umword_t stack)
{
// assert((((umword_t)user_sp) & 0x7UL) == 0);
umword_t usp = ((umword_t)(user_sp) & ~0x7UL);
pf_t *cur_pf = (pf_t *)(usp)-1; // thread_get_pf(cur_th);
cur_pf->pf_s.xpsr = 0x01000000L;
cur_pf->pf_s.lr = (umword_t)NULL; //!< 线程退出时调用的函数
cur_pf->pf_s.pc = (umword_t)pc | 0x1;
cur_pf->rg1[5] = (umword_t)ram;
cur_th->sp.knl_sp = ((char *)cur_th + THREAD_BLOCK_SIZE - 8);
cur_th->sp.user_sp = cur_pf;
cur_th->sp.sp_type = 0xfffffffd;
// printk("exc_regs:%x %x %x\n", cur_pf->pf_s.pc, cur_th->sp.user_sp, ram);
}

View File

@@ -0,0 +1,85 @@
/**
* @file mm_space.c
* @author ATShining (1358745329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "util.h"
#include "mm_space.h"
#include "mpu.h"
#include "assert.h"
#if CONFIG_MK_MPU_CFG
void mm_space_init(mm_space_t *mm_space, int is_knl)
{
for (int i = 0; i < CONFIG_REGION_NUM; i++)
{
mm_space->pt_regions[i].region_inx = -1;
}
}
region_info_t *mm_space_alloc_pt_region(mm_space_t *m_space)
{
for (int i = 0; i < CONFIG_REGION_NUM; i++)
{
/*TODO:原子操作*/
if (m_space->pt_regions[i].region_inx < 0)
{
m_space->pt_regions[i].region_inx = (int16_t)i;
return &m_space->pt_regions[i];
}
}
return NULL;
}
void mm_space_free_pt_region(mm_space_t *m_space, region_info_t *ri)
{
ri->region_inx = -1;
}
bool_t mm_space_add(mm_space_t *m_space,
umword_t addr,
umword_t size,
uint8_t attrs)
{
region_info_t *ri = mm_space_alloc_pt_region(m_space);
if (!ri)
{
return FALSE;
}
#if CONFIG_MPU_VERSION == 1
if (!is_power_of_2(size) || (addr & (!(size - 1))) != 0)
{
//!< 申请的大小必须是2的整数倍而且地址也必须是2的整数倍
mm_space_free_pt_region(m_space, ri);
return FALSE;
}
#elif CONFIG_MPU_VERSION == 2
if ((size & (MPU_ALIGN_SIZE - 1)) == 0 && (addr & (MPU_ALIGN_SIZE - 1)) == 0)
{
mm_space_free_pt_region(m_space, ri);
return FALSE;
}
#endif
mpu_calc_regs(ri, addr, size, attrs, 0);
ri->start_addr = addr;
ri->size = size;
return TRUE;
}
void mm_space_del(mm_space_t *m_space, umword_t addr)
{
for (int i = 0; i < CONFIG_REGION_NUM; i++)
{
if (m_space->pt_regions[i].region_inx >= 0 &&
m_space->pt_regions[i].start_addr == addr)
{
m_space->pt_regions[i].region_inx = -1;
m_space->pt_regions[i].start_addr = 0;
break;
}
}
}
#endif

View File

@@ -0,0 +1,25 @@
#include <scheduler.h>
#include <types.h>
#include <thread.h>
#include <util.h>
sp_info_t *schde_to(void *usp, void *ksp, umword_t sp_type)
{
scheduler_t *sche = scheduler_get_current();
sched_t *next = sche->cur_sche;
thread_t *next_th = container_of(next, thread_t, sche);
assert(next_th->magic == THREAD_MAGIC);
if (sched_reset)
{
thread_t *cur_th = thread_get_current();
cur_th->sp.knl_sp = ksp;
cur_th->sp.user_sp = usp;
cur_th->sp.sp_type = sp_type;
}
sched_reset = 1;
return &next_th->sp;
}

View File

@@ -12,7 +12,29 @@
#include "types.h"
#include "arch.h"
#define LOG_INTR_NO 37//USART1_IRQn
#define LOG_INTR_NO 37 // USART1_IRQn
/// @brief 线程信息
typedef struct
{
umword_t rg0[4]; //!< r0-r3
umword_t r12;
umword_t lr;
umword_t pc;
umword_t xpsr;
} pf_s_t;
typedef struct pf
{
umword_t rg1[8]; //!< r4-r11
pf_s_t pf_s;
} pf_t;
typedef struct sp_info
{
void *user_sp; //!< 用户态的sp
void *knl_sp; //!< 内核sp
mword_t sp_type; //!< 使用的栈类型
} sp_info_t;
#define read_reg(addr) (*((volatile umword_t *)(addr)))
#define write_reg(addr, data) \
@@ -43,7 +65,7 @@
: "=r"(ret) \
: \
:); \
((ret & 0x4) ? 0 : 1); \
((ret & 0x4) ? 0 : 1); \
})
void to_sche(void);
@@ -88,17 +110,17 @@ void arch_disable_irq(int inx);
void arch_enable_irq(int inx);
void arch_set_enable_irq_prio(int inx, int sub_prio, int pre_prio);
#define sti() \
#define sti() \
do \
{ \
write_sysreg(0, PRIMASK); \
} while (0)
#define cli() \
do \
{ \
__asm__ __volatile__("CPSID I\n" :: \
:); \
} while (0)
#define cli() \
do \
{ \
write_sysreg(0, PRIMASK); \
} while (0)
static inline void preemption(void)
{

View File

@@ -0,0 +1,55 @@
/**
* @file thread_armv7m.c
* @author ATShining (135874329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "thread.h"
#include "printk.h"
#include "thread_armv7m.h"
#include "app.h"
#include "mm_wrap.h"
#include "arch.h"
#include "string.h"
syscall_entry_func syscall_handler_get(void)
{
return syscall_entry;
}
void thread_knl_pf_set(thread_t *cur_th, void *pc)
{
pf_t *cur_pf = ((pf_t *)((char *)cur_th + THREAD_BLOCK_SIZE)) - 1;
cur_pf->pf_s.xpsr = 0x01000000L;
cur_pf->pf_s.lr = (umword_t)NULL; //!< 线程退出时调用的函数
cur_pf->pf_s.pc = (umword_t)pc | 0x1;
// cur_pf->rg1[5] = (umword_t)0;
cur_th->sp.knl_sp = (char *)cur_pf;
cur_th->sp.user_sp = 0;
cur_th->sp.sp_type = 0xfffffff9;
}
void thread_user_pf_set(thread_t *cur_th, void *pc, void *user_sp, void *ram, umword_t stack)
{
// assert((((umword_t)user_sp) & 0x7UL) == 0);
umword_t usp = ((umword_t)(user_sp) & ~0x7UL);
pf_t *cur_pf = (pf_t *)(usp)-1; // thread_get_pf(cur_th);
cur_pf->pf_s.xpsr = 0x01000000L;
cur_pf->pf_s.lr = (umword_t)NULL; //!< 线程退出时调用的函数
cur_pf->pf_s.pc = (umword_t)pc | 0x1;
cur_pf->rg1[5] = (umword_t)ram;
cur_th->sp.knl_sp = ((char *)cur_th + THREAD_BLOCK_SIZE - 8);
cur_th->sp.user_sp = cur_pf;
cur_th->sp.sp_type = 0xfffffffd;
// printk("exc_regs:%x %x %x\n", cur_pf->pf_s.pc, cur_th->sp.user_sp, ram);
}

View File

@@ -6,14 +6,17 @@ static umword_t sys_tick_cnt;
umword_t sys_tick_cnt_get(void)
{
return 0;
return sys_tick_cnt;
}
#include <asm/arm_local_reg.h>
extern void handle_timer_irq(void);
void SysTick_Handler(void)
{
handle_timer_irq(); // TODO:定时器的处理应该被分流,这里处理还有点问题,而且最好采用通用定时器
// 进行上下文切换
thread_sched();
sys_tick_cnt++;
thread_timeout_check(1);
futex_timeout_times_tick();
thread_sched();
}

View File

@@ -0,0 +1,4 @@
#pragma once
void knl_test(void);

View File

@@ -1,4 +1,3 @@
#pragma once
#include "mm_page.h"
void mm_man_dump(void);
void *mm_page_alloc_fault(mm_pages_t *mm, addr_t addr);
void mm_man_dump(void);

View File

@@ -1,49 +0,0 @@
#pragma once
#include "types.h"
#include "ram_limit.h"
#include "util.h"
typedef addr_t mm_addr_t;
struct region_info;
typedef struct region_info region_info_t;
// 使用一个mpu区域模拟页表
// 该文件实现内存的管理
typedef struct mm_entry
{
mm_addr_t addr; //!< 页表的地址
uint16_t pfn_nr; //!< 多少页
uint8_t attrs; //!< 属性
} mm_entry_t;
static inline void mm_entry_set_first(mm_addr_t *addr)
{
*addr |= 1UL;
}
static inline bool_t mm_entry_get_first(mm_addr_t addr)
{
return addr & 0x1UL;
}
static inline addr_t mm_entry_get_addr(mm_addr_t addr)
{
return MASK_LSB(addr, 2);
}
static inline void mm_entry_set_addr(mm_addr_t *addr, addr_t new_addr)
{
*addr &= ~3UL;
*addr |= new_addr & (~3UL);
}
#define PAGE_NR 64
#define PAGE_SIZE (1 << CONFIG_PAGE_SHIFT)
typedef struct mm_pages
{
mm_entry_t list[PAGE_NR]; // TODO: 使用hashmap
region_info_t *region; //!< pages使用的mpu region
} mm_pages_t;
void mm_pages_init(mm_pages_t *mm, region_info_t *regi);
int mm_pages_alloc_page(mm_pages_t *mm, ram_limit_t *lim, size_t pnf_nr, addr_t *alloc_addr, uint8_t attrs);
void mm_pages_free_page(mm_pages_t *mm, ram_limit_t *lim, addr_t addr, size_t pfn_nr);

View File

@@ -3,7 +3,11 @@
#include "types.h"
#include "mm_page.h"
#include <assert.h>
#include <util.h>
#if CONFIG_MK_MPU_CFG
#include <early_boot.h>
#endif
#if !IS_ENABLED(CONFIG_MMU)
typedef struct region_info
{
#if CONFIG_MK_MPU_CFG
@@ -17,16 +21,24 @@ typedef struct region_info
uint8_t region; //!< 区域禁止信息
#endif
} region_info_t;
#endif
typedef struct mm_space
{
#if CONFIG_MK_MPU_CFG
region_info_t pt_regions[CONFIG_REGION_NUM]; //!< mpu内存保护块
#endif
// mm_pages_t mm_pages; //!< 模拟分页内存
#if IS_ENABLED(CONFIG_MMU)
page_entry_t mem_dir; //!< MMU根映射表存放映射信息
#endif
void *mm_block; //!< task 的私有内存块
size_t mm_block_size; //!< 私有内存块的大小
} mm_space_t;
static inline page_entry_t *mm_space_get_pdir(mm_space_t *sp)
{
return &sp->mem_dir;
}
enum region_rights
{
REGION_PRIV = 1,
@@ -42,6 +54,7 @@ void mm_space_init(mm_space_t *mm_space, int is_knl);
bool_t mm_space_add(mm_space_t *m_space, umword_t addr, umword_t size, uint8_t attrs);
void mm_space_del(mm_space_t *m_space, umword_t addr);
#else
#if !IS_ENABLED(CONFIG_MMU)
static inline void mm_space_init(mm_space_t *mm_space, int is_knl)
{
}
@@ -52,8 +65,14 @@ static inline bool_t mm_space_add(mm_space_t *m_space, umword_t addr, umword_t s
static inline void mm_space_del(mm_space_t *m_space, umword_t addr)
{
}
#else
void mm_space_init(mm_space_t *mm_space, int is_knl);
bool_t mm_space_add(mm_space_t *m_space, umword_t addr, umword_t size, uint8_t attrs);
void mm_space_del(mm_space_t *m_space, umword_t addr);
#endif
#endif
#if !IS_ENABLED(CONFIG_MMU)
static inline void mm_space_set_ram_block(mm_space_t *mm_space, void *mem, size_t size)
{
mm_space->mm_block = mem;
@@ -66,3 +85,11 @@ static inline void mm_space_get_ram_block(mm_space_t *mm_space, void **mem, size
*mem = mm_space->mm_block;
*size = mm_space->mm_block_size;
}
#else
static inline void mm_space_set_ram_block(mm_space_t *mm_space, void *mem, size_t size)
{
}
static inline void mm_space_get_ram_block(mm_space_t *mm_space, void **mem, size_t *size)
{
}
#endif

View File

@@ -17,6 +17,7 @@ void mpu_calc_regs(region_info_t *region, umword_t addr, umword_t ffs_val,
void mpu_switch_to(void);
void mpu_switch_to_task(struct task *tk);
#else
#if !IS_ENABLED(CONFIG_MMU)
static inline void mpu_init(void)
{
}
@@ -43,4 +44,9 @@ static inline void mpu_switch_to(void)
static inline void mpu_switch_to_task(struct task *tk)
{
}
#else
static inline void mpu_switch_to_task(struct task *tk)
{
}
#endif
#endif

View File

@@ -11,7 +11,7 @@
#include "slist.h"
#define PRIO_MAX WORD_BITS
extern umword_t sched_reset;
typedef struct sched
{
int prio;

View File

@@ -89,26 +89,7 @@ enum thread_ipc_state
THREAD_TIMEOUT,
THREAD_IPC_ABORT,
};
typedef struct
{
umword_t rg0[4]; //!< r0-r3
umword_t r12;
umword_t lr;
umword_t pc;
umword_t xpsr;
} pf_s_t;
typedef struct pf
{
umword_t rg1[8]; //!< r4-r11
pf_s_t pf_s;
} pf_t;
typedef struct sp_info
{
void *user_sp; //!< 用户态的sp
void *knl_sp; //!< 内核sp
mword_t sp_type; //!< 使用的栈类型
} sp_info_t;
typedef struct msg_buf
{

View File

@@ -1,5 +1,4 @@
#pragma once
void thread_exit(void);
void thread_knl_pf_set(thread_t *cur_th, void *pc);
void thread_user_pf_set(thread_t *cur_th, void *pc, void *user_sp, void *ram, umword_t stack);
void task_knl_init(task_t *knl_tk);

View File

@@ -20,7 +20,7 @@ umword_t cpulock_lock(void)
umword_t res;
res = intr_status();
sti();
cli();
return res;
}
/**
@@ -44,10 +44,10 @@ void cpulock_set(umword_t s)
{
if (s)
{
sti();
cli();
}
else
{
cli();
sti();
}
}

View File

@@ -83,6 +83,10 @@ void entry_handler(void)
{
umword_t isr_no = arch_get_isr_no();
if (isr_no <= 0)
{
return;
}
isr_no -= CONFIG_USER_ISR_START_NO; //!< 系统用的irq偏移
assert(isr_no < CONFIG_IRQ_REG_TAB_SIZE);
@@ -93,7 +97,9 @@ void entry_handler(void)
{
irqs[isr_no].irq_tigger_func(&irqs[isr_no]);
}
} else {
}
else
{
arch_disable_irq(isr_no);
}
}

56
mkrtos_knl/knl/knl_test.c Normal file
View File

@@ -0,0 +1,56 @@
#include <task.h>
#include <thread.h>
#include <factory.h>
#include <thread_task_arch.h>
static void th_test(void *arg)
{
int a[20];
for (int i = 0; i < 20; i++)
{
a[i] = i;
}
while (1)
{
for (int i = 0; i < 20; i++)
{
printk("%d ", a[i]);
}
printk("\n");
}
}
static void th_test2(void *arg)
{
int a[20];
for (int i = 0; i < 20; i++)
{
a[i] = i;
}
while (1)
{
for (int i = 0; i < 20; i++)
{
printk("%d,", a[i]);
}
printk("\n");
}
}
void knl_test(void)
{
thread_t *thread2;
task_t *cur_tk = thread_get_current_task();
thread2 = thread_create(&root_factory_get()->limit);
assert(thread2);
thread_bind(thread2, &cur_tk->kobj);
thread_knl_pf_set(thread2, th_test);
thread_ready(thread2, FALSE);
thread_t *thread3;
thread3 = thread_create(&root_factory_get()->limit);
assert(thread3);
thread_bind(thread3, &cur_tk->kobj);
thread_knl_pf_set(thread3, th_test2);
thread_ready(thread3, FALSE);
}

View File

@@ -13,10 +13,10 @@
#include "mm.h"
#include "mm_space.h"
#include "util.h"
#include "mpu.h"
#include "printk.h"
#if CONFIG_MK_MPU_CFG
#include "mpu.h"
static bool_t mpu_calc(
mm_space_t *ms,
umword_t mem_start_addr,

View File

@@ -1,132 +0,0 @@
/**
* @file mm_page.c
* @author ATShining (1358745329@qq.com)
* @brief
* @version 0.1
* @date 2023-09-29
*
* @copyright Copyright (c) 2023
*
*/
#include "types.h"
#include "mm_space.h"
#include "mm_wrap.h"
#include "assert.h"
#include "err.h"
#include "mm_page.h"
#include "mm_space.h"
#include "mpu.h"
#if CONFIG_MK_MPU_CFG
static mm_entry_t *mm_pages_entry_alloc(mm_pages_t *mm, addr_t new_addr)
{
for (int i = 0; i < PAGE_NR; i++)
{
if (mm->list[i].addr == 0)
{
mm_entry_set_addr(&mm->list[i].addr, new_addr);
return &mm->list[i];
}
}
return NULL;
}
static void mm_pages_entry_free(mm_pages_t *mm, addr_t free_addr)
{
for (int i = 0; i < PAGE_NR; i++)
{
if (mm_entry_get_addr(mm->list[i].addr) == free_addr)
{
mm->list[i].addr = 0;
}
}
}
static mm_entry_t *mm_pages_find_first(mm_pages_t *mm, addr_t free_addr)
{
for (int i = 0; i < PAGE_NR; i++)
{
if (mm_entry_get_addr(mm->list[i].addr) == free_addr && mm_entry_get_first(mm->list[i].addr))
{
return &mm->list[i];
}
}
return NULL;
}
void mm_pages_init(mm_pages_t *mm, region_info_t *regi)
{
for (int i = 0; i < PAGE_NR; i++)
{
mm->list[i].addr = 0;
}
mm->region = regi;
}
int mm_pages_alloc_page(mm_pages_t *mm, ram_limit_t *lim, size_t pnf_nr, addr_t *alloc_addr, uint8_t attrs)
{
assert(mm);
assert(alloc_addr);
void *mem = mm_limit_alloc_align(lim, pnf_nr * PAGE_SIZE, PAGE_SIZE);
if (!mem)
{
return -ENOMEM;
}
for (int i = 0; i < pnf_nr; i++)
{
mm_entry_t *mm_entry = mm_pages_entry_alloc(mm, (addr_t)mem + i * PAGE_SIZE);
if (mm_entry == NULL)
{
mm_limit_free_align(lim, mem, pnf_nr * PAGE_SIZE);
/*TODO:清除申请的所有*/
return -ENOMEM;
}
if (i == 0)
{
mm_entry_set_first(&mm_entry->addr);
mm_entry->attrs = attrs;
mm_entry->pfn_nr = pnf_nr;
}
else
{
mm_entry->attrs = attrs;
mm_entry->pfn_nr = 1;
}
}
*alloc_addr = (addr_t)mem;
return 0;
}
void mm_pages_free_page(mm_pages_t *mm, ram_limit_t *lim, addr_t addr, size_t pfn_nr)
{
addr = ALIGN_DOWN(addr, PAGE_SIZE);
mm_entry_t *mm_entry = mm_pages_find_first(mm, addr);
if (!mm_entry)
{
return;
}
addr_t start_addr = mm_entry_get_addr(mm_entry->addr);
mm_limit_free_align(lim, (void *)start_addr, PAGE_SIZE * mm_entry->pfn_nr);
for (int i = 0; i < pfn_nr; i++)
{
mm_pages_entry_free(mm, start_addr);
start_addr += PAGE_SIZE;
}
mpu_region_clr(mm->region->region_inx);
}
void *mm_page_alloc_fault(mm_pages_t *mm, addr_t addr)
{
addr = ALIGN_DOWN(addr, PAGE_SIZE);
for (int i = 0; i < PAGE_NR; i++)
{
if (mm_entry_get_addr(mm->list[i].addr) == addr)
{
mpu_calc_regs(mm->region, addr, PAGE_SIZE,
mm->list[i].attrs, 0x0);
mpu_region_set(mm->region->region_inx, mm->region->rbar,
mm->region->rasr);
return (void *)(mm->list[i].addr);
}
}
return NULL;
}
#endif

View File

@@ -13,6 +13,7 @@
#include "assert.h"
#include "thread.h"
#include "init.h"
#include <arch.h>
static scheduler_t scheduler;
umword_t sched_reset = 0;
@@ -111,26 +112,7 @@ sched_t *scheduler_next(void)
return next_sch;
}
sp_info_t *schde_to(void *usp, void *ksp, umword_t sp_type)
{
scheduler_t *sche = scheduler_get_current();
sched_t *next = sche->cur_sche;
thread_t *next_th = container_of(next, thread_t, sche);
assert(next_th->magic == THREAD_MAGIC);
if (sched_reset)
{
thread_t *cur_th = thread_get_current();
cur_th->sp.knl_sp = ksp;
cur_th->sp.user_sp = usp;
cur_th->sp.sp_type = sp_type;
}
sched_reset = 1;
return &next_th->sp;
}
void sched_tail(void)
{
/*TODO:*/
sti();
}

View File

@@ -21,6 +21,9 @@
#include "assert.h"
#include "access.h"
#include "printk.h"
#if IS_ENABLED(CONFIG_MMU)
#include "early_boot.h"
#endif
/**
* @brief 任务的操作码
*
@@ -334,7 +337,12 @@ void task_init(task_t *task, ram_limit_t *ram, int is_knl)
task->kobj.put_func = task_put;
task->kobj.stage_1_func = task_release_stage1;
task->kobj.stage_2_func = task_release_stage2;
#if IS_ENABLED(CONFIG_MMU)
knl_pdir_init(&task->mm_space.mem_dir, task->mm_space.mem_dir.dir, 3);
#else
mm_space_add(&task->mm_space, CONFIG_KNL_TEXT_ADDR, CONFIG_KNL_TEXT_SIZE, REGION_RO); // TODO:这里应该用config.配置
#endif
}
static bool_t task_put(kobject_t *kobj)

View File

@@ -20,12 +20,13 @@
#include "task.h"
#include "thread.h"
#include "slist.h"
#include "thread_armv7m.h"
#include "thread_task_arch.h"
#include "assert.h"
#include "err.h"
#include "map.h"
#include "access.h"
#include "ipc.h"
#include "asm/mm.h"
enum thread_op
{
SET_EXEC_REGS,

View File

@@ -20,9 +20,13 @@
#include "map.h"
#include "app.h"
#include "mm_wrap.h"
#include "thread_armv7m.h"
#include "thread_task_arch.h"
#include "knl_misc.h"
#if IS_ENABLED(CONFIG_KNL_TEST)
#include <knl_test.h>
#endif
static uint8_t knl_msg_buf[THREAD_MSG_BUG_LEN];
static thread_t *knl_thread;
static task_t knl_task;
@@ -36,7 +40,7 @@ static void knl_main(void)
{
umword_t status;
umword_t status2;
// printk("knl main run..\n");
printk("knl main run..\n");
while (1)
{
task_t *pos;
@@ -87,7 +91,7 @@ static void knl_init_1(void)
thread_init(knl_thread, &root_factory_get()->limit);
task_init(&knl_task, &root_factory_get()->limit, TRUE);
task_knl_init(&knl_task);
thread_knl_pf_set(knl_thread, knl_main);
thread_bind(knl_thread, &knl_task.kobj);
thread_set_msg_bug(knl_thread, knl_msg_buf);
@@ -97,6 +101,7 @@ static void knl_init_1(void)
}
INIT_STAGE1(knl_init_1);
/**
* 初始化init线程
* 初始化用户态任务
@@ -107,12 +112,13 @@ static void knl_init_2(void)
{
mm_trace();
#if IS_ENABLED(CONFIG_KNL_TEST)
knl_test();
#else
init_thread = thread_create(&root_factory_get()->limit);
assert(init_thread);
init_task = task_create(&root_factory_get()->limit, FALSE);
assert(init_task);
#if 0
app_info_t *app = app_info_get((void *)(CONFIG_KNL_TEXT_ADDR + CONFIG_INIT_TASK_OFFSET));
// 申请init的ram内存
assert(task_alloc_base_ram(init_task, &root_factory_get()->limit, app->i.ram_size + THREAD_MSG_BUG_LEN) >= 0);
@@ -195,5 +201,4 @@ void start_kernel(void)
while (1)
;
// printk(".");
}

View File

@@ -0,0 +1,10 @@
#!/bin/bash
# -machine virt,virtualization=on,gic-version=2,highmem=off,secure=off,dumpdtb=virt.dtb
qemu-system-aarch64 \
-machine virt,virtualization=on,gic-version=2,highmem=off,secure=off\
-cpu cortex-a53 \
-nographic \
-m size=2048 \
-smp 4\
-kernel $PWD/build/output/bootstrap.elf