rpi400: initial platform support

This commit is contained in:
K. Lange 2022-02-16 12:36:03 +09:00
parent e90278b04b
commit 116ee0a803
9 changed files with 906 additions and 39 deletions

2
.gitignore vendored
View File

@ -33,3 +33,5 @@
/boot/mbr.sys
/bootstub
/.arch
/kernel8.img
/kernel8.img.elf

View File

@ -5,7 +5,7 @@ ARCH_KERNEL_CFLAGS = -z max-page-size=0x1000 -nostdlib -mgeneral-regs-only -mno-
TARGET=aarch64-unknown-toaru
all: system
system: misaka-kernel ramdisk.igz bootstub | $(BUILD_KRK)
system: misaka-kernel ramdisk.igz bootstub kernel8.img | $(BUILD_KRK)
misaka-kernel: ${KERNEL_ASMOBJS} ${KERNEL_OBJS} kernel/symbols.o kernel/arch/aarch64/link.ld
${CC} -g -T kernel/arch/${ARCH}/link.ld ${KERNEL_CFLAGS} -o $@ ${KERNEL_ASMOBJS} ${KERNEL_OBJS} kernel/symbols.o
@ -15,7 +15,17 @@ BOOTSTUB_OBJS += $(patsubst %.S,%.o,$(wildcard kernel/arch/aarch64/bootstub/*.S)
BOOTSTUB_OBJS += kernel/misc/kprintf.o kernel/misc/string.o
bootstub: ${BOOTSTUB_OBJS} kernel/arch/aarch64/bootstub/link.ld
${CC} -g -T kernel/arch/aarch64/bootstub/link.ld ${KERNEL_CFLAGS} -o $@ ${BOOTSTUB_OBJS} -lgcc
${CC} -g -T kernel/arch/aarch64/bootstub/link.ld ${KERNEL_CFLAGS} -o $@ ${BOOTSTUB_OBJS}
RPI400_OBJS = $(patsubst %.c,%.o,$(wildcard kernel/arch/aarch64/rpi400/*.c))
RPI400_OBJS += $(patsubst %.S,%.o,$(wildcard kernel/arch/aarch64/rpi400/*.S))
RPI400_OBJS += kernel/misc/kprintf.o kernel/misc/string.o
kernel/arch/aarch64/rpi400/start.o: misaka-kernel ramdisk.igz
kernel8.img: ${RPI400_OBJS} kernel/arch/aarch64/rpi400/link.ld
${CC} -g -T kernel/arch/aarch64/rpi400/link.ld ${KERNEL_CFLAGS} -o $@.elf ${RPI400_OBJS}
${OC} $@.elf -O binary $@
QEMU = qemu-system-aarch64

View File

@ -437,8 +437,8 @@ static void bootstub_start_kernel(Elf64_Header * header) {
printf("bootstub: Jump to kernel entry point at %zx\n",
header->e_entry);
void (*entry)(uintptr_t,uintptr_t) = (void(*)(uintptr_t,uintptr_t))header->e_entry;
entry(QEMU_DTB_BASE, KERNEL_PHYS_BASE);
void (*entry)(uintptr_t,uintptr_t,uintptr_t) = (void(*)(uintptr_t,uintptr_t,uintptr_t))header->e_entry;
entry(QEMU_DTB_BASE, KERNEL_PHYS_BASE, 0);
}
int kmain(void) {

View File

@ -24,6 +24,7 @@
#include <kernel/misc.h>
#include <kernel/ptrace.h>
#include <kernel/ksym.h>
#include <kernel/gzip.h>
#include <sys/ptrace.h>
@ -82,7 +83,7 @@ size_t arch_cpu_mhz(void) {
static void arch_clock_initialize() {
/* QEMU RTC */
void * clock_addr = mmu_map_from_physical(0x09010000);
//void * clock_addr = mmu_map_from_physical(0x09010000);
/* Get frequency of system timer */
uint64_t val;
@ -90,7 +91,8 @@ static void arch_clock_initialize() {
sys_timer_freq = val / 10000;
/* Get boot time from RTC */
arch_boot_time = *(volatile uint32_t*)clock_addr;
//arch_boot_time = *(volatile uint32_t*)clock_addr;
arch_boot_time = 1644908027UL;
/* Get the "basis time" - the perf timestamp we got the wallclock time at */
basis_time = arch_perf_timer() / sys_timer_freq;
@ -143,9 +145,11 @@ void relative_time(unsigned long seconds, unsigned long subseconds, unsigned lon
static void set_tick(void) {
asm volatile (
"mrs x0, CNTFRQ_EL0\n"
"mov x1, 100\n"
"mov x1, 100\n" // without this, one second
"udiv x0, x0, x1\n"
"msr CNTV_TVAL_EL0, x0\n"
"mov x0, 1\n"
"msr CNTV_CTL_EL0, x0\n"
:::"x0","x1");
}
@ -156,11 +160,6 @@ void timer_start(void) {
/* Enable the local timer */
set_tick();
asm volatile (
"mov x0, 1\n"
"msr CNTV_CTL_EL0, x0\n"
:::"x0");
/* This is global, we only need to do this once... */
gic_regs[0] = 1;
@ -253,15 +252,22 @@ static void dtb_locate_cmdline(void) {
static void exception_handlers(void) {
extern char _exception_vector[];
const uintptr_t gic_base = (uintptr_t)mmu_map_from_physical(0x08000000); /* TODO get this from dtb */
gic_regs = (volatile uint32_t*)gic_base;
const uintptr_t gicc_base = (uintptr_t)mmu_map_from_physical(0x08010000);
gicc_regs = (volatile uint32_t*)gicc_base;
asm volatile("msr VBAR_EL1, %0" :: "r"(&_exception_vector));
}
#if 0
#define GICD_BASE 0x08000000
#define GICC_BASE 0x08010000
#else
#define GICD_BASE 0xff841000
#define GICC_BASE 0xff842000
#endif
static void gic_map_regs(void) {
gic_regs = (volatile uint32_t*)mmu_map_mmio_region(GICD_BASE, 0x1000);
gicc_regs = (volatile uint32_t*)mmu_map_mmio_region(GICC_BASE, 0x2000);
}
void aarch64_sync_enter(struct regs * r) {
uint64_t esr, far, elr, spsr;
asm volatile ("mrs %0, ESR_EL1" : "=r"(esr));
@ -269,6 +275,20 @@ void aarch64_sync_enter(struct regs * r) {
asm volatile ("mrs %0, ELR_EL1" : "=r"(elr));
asm volatile ("mrs %0, SPSR_EL1" : "=r"(spsr));
#if 0
dprintf("EL0-EL1 sync: %d (%s) ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n",
this_core ? (this_core->current_process ? this_core->current_process->id : -1) : -1,
this_core ? (this_core->current_process ? this_core->current_process->name : "?") : "?",
esr, far, elr, spsr);
#endif
if (esr == 0x2000000) {
dprintf("Unknown exception: ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n", esr, far, elr, spsr);
dprintf("Instruction at ELR: 0x%08x\n", *(uint32_t*)elr);
while (1);
}
if (this_core->current_process) {
this_core->current_process->time_switch = arch_perf_timer();
}
@ -301,6 +321,8 @@ void aarch64_sync_enter(struct regs * r) {
/* System call */
if ((esr >> 26) == 0x15) {
//dprintf("pid %d syscall %zd elr=%#zx\n",
// this_core->current_process->id, r->x0, elr);
extern void syscall_handler(struct regs *);
syscall_handler(r);
return;
@ -320,16 +342,20 @@ void aarch64_sync_enter(struct regs * r) {
asm volatile ("mrs %0, TPIDR_EL0" : "=r"(tpidr_el0));
dprintf(" TPIDR_EL0=%#zx\n", tpidr_el0);
while (1);
send_signal(this_core->current_process->id, SIGSEGV, 1);
}
char _ret_from_preempt_source[1];
#define EOI(x) do { gicc_regs[4] = (x); } while (0)
#define EOI(x) do { \
gicc_regs[4] = (x); \
} while (0)
static void aarch64_interrupt_dispatch(int from_wfi) {
uint32_t iar = gicc_regs[3];
uint32_t irq = iar & 0x3FF;
//uint32_t cpu = (iar >> 10) & 0x7;
uint32_t cpu = (iar >> 10) & 0x7;
switch (irq) {
case TIMER_IRQ:
@ -400,7 +426,9 @@ void aarch64_fault_enter(struct regs * r) {
asm volatile ("mrs %0, SPSR_EL1" : "=r"(spsr));
dprintf("EL1-EL1 fault handler, core %d\n", this_core->cpu_id);
dprintf("In process %d (%s)\n", this_core->current_process->id, this_core->current_process->name);
if (this_core && this_core->current_process) {
dprintf("In process %d (%s)\n", this_core->current_process->id, this_core->current_process->name);
}
dprintf("ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n", esr, far, elr, spsr);
aarch64_regs(r);
@ -414,6 +442,11 @@ void aarch64_fault_enter(struct regs * r) {
while (1);
}
void aarch64_sp0_fault_enter(struct regs * r) {
dprintf("EL1-EL1 sp0 entry?\n");
while (1);
}
/**
* @brief Enable FPU and NEON (SIMD)
*
@ -437,6 +470,7 @@ void arch_pause(void) {
* the interrupt function won't be called, so we'll need to change
* it once we start getting actual hardware interrupts. */
asm volatile ("wfi");
aarch64_interrupt_dispatch(1);
}
@ -476,6 +510,17 @@ static void symbols_install(void) {
}
}
struct rpitag {
uint32_t phys_addr;
uint32_t x;
uint32_t y;
uint32_t s;
uint32_t b;
uint32_t size;
uint32_t ramdisk_start;
uint32_t ramdisk_end;
};
/**
* Main kernel C entrypoint for qemu's -machine virt
*
@ -485,10 +530,7 @@ static void symbols_install(void) {
* at -2GiB, and there's some other mappings so that
* a bit of RAM is 1:1.
*/
int kmain(uintptr_t dtb_base, uintptr_t phys_base) {
early_log_initialize();
console_set_output(_early_log_write);
int kmain(uintptr_t dtb_base, uintptr_t phys_base, uintptr_t rpi_tag) {
extern uintptr_t aarch64_kernel_phys_base;
aarch64_kernel_phys_base = phys_base;
@ -496,6 +538,29 @@ int kmain(uintptr_t dtb_base, uintptr_t phys_base) {
extern uintptr_t aarch64_dtb_phys;
aarch64_dtb_phys = dtb_base;
if (rpi_tag) {
extern uint8_t * lfb_vid_memory;
extern uint16_t lfb_resolution_x;
extern uint16_t lfb_resolution_y;
extern uint16_t lfb_resolution_b;
extern uint32_t lfb_resolution_s;
extern size_t lfb_memsize;
struct rpitag * tag = (struct rpitag*)rpi_tag;
lfb_vid_memory = mmu_map_from_physical(tag->phys_addr);
lfb_resolution_x = tag->x;
lfb_resolution_y = tag->y;
lfb_resolution_s = tag->s;
lfb_resolution_b = tag->b;
lfb_memsize = tag->size;
fbterm_initialize();
} else {
early_log_initialize();
console_set_output(_early_log_write);
}
dprintf("%s %d.%d.%d-%s %s %s\n",
__kernel_name,
__kernel_version_major,
@ -520,21 +585,61 @@ int kmain(uintptr_t dtb_base, uintptr_t phys_base) {
/* Load ramdisk over fw-cfg. */
uintptr_t ramdisk_phys_base = 0;
size_t ramdisk_size = 0;
fwcfg_load_initrd(&ramdisk_phys_base, &ramdisk_size);
if (rpi_tag) {
struct rpitag * tag = (struct rpitag*)rpi_tag;
extern char end[];
dprintf("rpi: compressed ramdisk is at %#x \n", tag->ramdisk_start);
dprintf("rpi: end of ramdisk is at %#x \n", tag->ramdisk_end);
dprintf("rpi: uncompress ramdisk to %#zx \n", (uintptr_t)&end);
uint32_t size;
memcpy(&size, (void*)(uintptr_t)(tag->ramdisk_end - sizeof(uint32_t)), sizeof(uint32_t));
dprintf("rpi: size of uncompressed ramdisk is %#x\n", size);
/* Probe DTB for memory layout. */
extern char end[];
size_t memaddr, memsize;
dtb_memory_size(&memaddr, &memsize);
gzip_inputPtr = (uint8_t*)(uintptr_t)tag->ramdisk_start;
gzip_outputPtr = (uint8_t*)&end;
/* Initialize the MMU based on the memory we got from dtb */
mmu_init(
memaddr, memsize,
0x40100000 /* Should be end of DTB, but we're really just guessing */,
(uintptr_t)&end + ramdisk_size - 0xffffffff80000000UL);
if (gzip_decompress()) {
dprintf("rpi: gzip failure, not mounting ramdisk\n");
while (1);
}
/* Find the cmdline */
dtb_locate_cmdline();
dprintf("rpi: ramdisk decompressed\n");
for (size_t i = 0; i < size; i += 64) {
asm volatile ("dc cvac, %0\n" :: "r"((uintptr_t)&end + i) : "memory");
}
ramdisk_phys_base = mmu_map_to_physical(NULL, (uintptr_t)&end);
ramdisk_size = size;
dprintf("rpi: ramdisk_phys_base set to %#zx\n", ramdisk_phys_base);
mmu_init(0, 512 * 1024 * 1024,
0x80000,
(uintptr_t)&end + ramdisk_size - 0xffffffff80000000UL);
dprintf("rpi: mmu reinitialized\n");
_arch_args = "vid=preset start=live-session root=/dev/ram0";
} else {
fwcfg_load_initrd(&ramdisk_phys_base, &ramdisk_size);
/* Probe DTB for memory layout. */
extern char end[];
size_t memaddr, memsize;
dtb_memory_size(&memaddr, &memsize);
/* Initialize the MMU based on the memory we got from dtb */
mmu_init(
memaddr, memsize,
0x40100000 /* Should be end of DTB, but we're really just guessing */,
(uintptr_t)&end + ramdisk_size - 0xffffffff80000000UL);
/* Find the cmdline */
dtb_locate_cmdline();
}
gic_map_regs();
/* Set up all the other arch-specific stuff here */
fpu_enable();
@ -545,7 +650,10 @@ int kmain(uintptr_t dtb_base, uintptr_t phys_base) {
/* Initialize the framebuffer and fbterm here */
framebuffer_initialize();
fbterm_initialize();
if (!rpi_tag) {
fbterm_initialize();
}
/* Ramdisk */
ramdisk_mount(ramdisk_phys_base, ramdisk_size);
@ -554,17 +662,20 @@ int kmain(uintptr_t dtb_base, uintptr_t phys_base) {
aarch64_processor_data();
/* Start other cores here */
aarch64_smp_start();
//aarch64_smp_start();
processor_count = 1;
/* Set up the system virtual timer to produce interrupts for userspace scheduling */
timer_start();
#if 0
/* Install drivers that may need to sleep here */
virtio_input();
/* Set up serial input */
extern void pl011_start(void);
pl011_start();
#endif
generic_main();

View File

@ -0,0 +1,275 @@
#include <stdint.h>
#include <kernel/string.h>
#include <kernel/printf.h>
static int fbterm_scroll = 0;
static void (*write_char)(int, int, int, uint32_t) = NULL;
static int (*get_width)(void) = NULL;
static int (*get_height)(void) = NULL;
static void (*scroll_terminal)(void) = NULL;
static int x = 0;
static int y = 0;
static int term_state = 0;
static char term_buf[1024] = {0};
static int term_buf_c = 0;
/* Is this in a header somewhere? */
extern uint8_t * lfb_vid_memory;
extern uint16_t lfb_resolution_x;
extern uint16_t lfb_resolution_y;
extern uint16_t lfb_resolution_b;
extern uint32_t lfb_resolution_s;
extern size_t lfb_memsize;
/* Bitmap font details */
#include "../../../../apps/terminal-font.h"
#define char_height LARGE_FONT_CELL_HEIGHT
#define char_width LARGE_FONT_CELL_WIDTH
/* Default colors */
#define BG_COLOR 0xFF000000 /* Background */
#define FG_COLOR 0xFFCCCCCC /* Main text color */
static uint32_t fg_color = FG_COLOR;
static uint32_t bg_color = BG_COLOR;
extern uint32_t lfb_resolution_s;
static inline void set_point(int x, int y, uint32_t value) {
if (lfb_resolution_b == 32) {
((uint32_t*)lfb_vid_memory)[y * (lfb_resolution_s/4) + x] = value;
} else if (lfb_resolution_b == 24) {
lfb_vid_memory[y * lfb_resolution_s + x * 3 + 0] = (value >> 0) & 0xFF;
lfb_vid_memory[y * lfb_resolution_s + x * 3 + 1] = (value >> 8) & 0xFF;
lfb_vid_memory[y * lfb_resolution_s + x * 3 + 2] = (value >> 16) & 0xFF;
}
}
static void fb_write_char(int _x, int _y, int val, uint32_t color) {
if (val > 128) {
val = 4;
}
int x = 1 + _x * char_width;
int y = _y * char_height;
uint16_t * c = large_font[val];
for (uint8_t i = 0; i < char_height; ++i) {
for (uint8_t j = 0; j < char_width; ++j) {
if (c[i] & (1 << (LARGE_FONT_MASK-j))) {
set_point(x+j,y+i,color);
} else {
set_point(x+j,y+i,bg_color);
}
}
}
}
/**
* @brief Basic 16-color ANSI palette with Tango colors.
*/
static uint32_t term_colors[] = {
0xFF000000,
0xFFCC0000,
0xFF4E9A06,
0xFFC4A000,
0xFF3465A4,
0xFF75507B,
0xFF06989A,
0xFFD3D7CF,
0xFF555753,
0xFFEF2929,
0xFF8AE234,
0xFFFCE94F,
0xFF729FCF,
0xFFAD7FA8,
0xFF34E2E2,
0xFFEEEEEC,
};
static int fb_get_width(void) {
return (lfb_resolution_x - 1) / char_width;
}
static int fb_get_height(void) {
return lfb_resolution_y / char_height;
}
static void fb_scroll_terminal(void) {
memmove(lfb_vid_memory, lfb_vid_memory + sizeof(uint32_t) * lfb_resolution_x * char_height, (lfb_resolution_y - char_height) * lfb_resolution_x * 4);
memset(lfb_vid_memory + sizeof(uint32_t) * (lfb_resolution_y - char_height) * lfb_resolution_x, 0x00, char_height * lfb_resolution_x * 4);
}
static void draw_square(int x, int y) {
int center_x = lfb_resolution_x / 2;
int center_y = lfb_resolution_y / 2;
for (size_t _y = 0; _y < 7; ++_y) {
uint32_t color = 0xFF00B2FF - (y * 8 + _y) * 0x200;
for (size_t _x = 0; _x < 7; ++_x) {
set_point(center_x - 32 + x * 8 + _x, center_y - 32 + y * 8 + _y, color);
}
}
}
static void fbterm_draw_logo(void) {
uint64_t logo_squares = 0x981818181818FFFFUL;
for (size_t y = 0; y < 8; ++y) {
for (size_t x = 0; x < 8; ++x) {
if (logo_squares & (1 << x)) {
draw_square(x,y);
}
}
logo_squares >>= 8;
}
}
static void fbterm_init_framebuffer(void) {
write_char = fb_write_char;
get_width = fb_get_width;
get_height = fb_get_height;
scroll_terminal = fb_scroll_terminal;
fbterm_draw_logo();
}
static void cursor_update(void) {
if (x >= get_width()) {
x = 0;
y++;
}
if (y >= get_height()) {
if (fbterm_scroll) {
y--;
scroll_terminal();
} else {
y = 0;
}
}
}
static void process_char(char ch) {
if (term_state == 1) {
if (ch == '[') {
term_buf_c = 0;
term_buf[term_buf_c] = '\0';
term_state = 2;
} else {
term_state = 0;
process_char(ch);
}
return;
} else if (term_state == 2) {
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
/* do the thing */
switch (ch) {
case 'm': {
char * arg = &term_buf[0];
char * next;
int argC = 0;
int isBold = 0;
do {
next = strchr(arg, ';');
if (next) { *next = '\0'; next++; }
int asInt = atoi(arg);
if (asInt == 0) {
fg_color = FG_COLOR;
bg_color = BG_COLOR;
isBold = 0;
} else if (asInt == 1) {
isBold = 1;
} else if (asInt == 22) {
fg_color = FG_COLOR;
isBold = 0;
} else if (asInt >= 30 && asInt <= 37) {
fg_color = term_colors[asInt-30 + (isBold ? 8 : 0)];
} else if (asInt >= 90 && asInt <= 97) {
fg_color = term_colors[asInt-90 + 8];
} else if (asInt >= 40 && asInt <= 47) {
bg_color = term_colors[asInt-40 + (isBold ? 8 : 0)];
} else if (asInt >= 100 && asInt <= 107) {
bg_color = term_colors[asInt-100 + 8];
} else if (asInt == 38) {
fg_color = FG_COLOR;
} else if (asInt == 48) {
bg_color = BG_COLOR;
} else if (asInt == 7) {
uint32_t tmp = fg_color;
fg_color = bg_color;
bg_color = tmp;
}
arg = next;
argC++;
} while (arg);
break;
}
case 'G': {
/* Set cursor column */
x = atoi(term_buf) - 1;
break;
}
case 'K': {
if (atoi(term_buf) == 0) {
for (int i = x; i < get_width(); ++i) {
write_char(i,y,' ',bg_color);
}
}
break;
}
}
term_state = 0;
} else {
term_buf[term_buf_c++] = ch;
term_buf[term_buf_c] = '\0';
}
return;
} else if (ch == '\033') {
term_state = 1;
return;
}
write_char(x,y,' ',bg_color);
switch (ch) {
case '\n':
x = 0;
y++;
break;
case '\r':
x = 0;
break;
case '\b':
if (x) {
x--;
write_char(x,y,' ',fg_color);
}
break;
default:
if ((unsigned int)ch > 127) return;
write_char(x,y,ch,fg_color);
x++;
break;
}
cursor_update();
}
static size_t (*previous_writer)(size_t,uint8_t*) = NULL;
size_t fbterm_write(size_t size, uint8_t *buffer) {
if (!buffer) return 0;
for (unsigned int i = 0; i < size; ++i) {
process_char(buffer[i]);
}
if (previous_writer) previous_writer(size,buffer);
return size;
}
void fbterm_initialize(void) {
if (!lfb_resolution_x) {
return;
}
fbterm_init_framebuffer();
previous_writer = printf_output;
printf_output = fbterm_write;
printf("fbterm: Generic framebuffer text output enabled.\n");
}

View File

@ -0,0 +1,56 @@
OUTPUT_FORMAT(elf64-littleaarch64)
ENTRY(start)
SECTIONS
{
. = 0x80000;
phys = .;
.text BLOCK(4K) : ALIGN(4K)
{
*(.bootstrap)
code = .;
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
data = .;
*(.data)
*(.symbols)
PROVIDE(kernel_symbols_start = .);
PROVIDE(kernel_symbols_end = .);
PROVIDE(bss_start = .);
}
__bss_start = .;
.bss BLOCK(4K) : ALIGN(4K)
{
bss = .;
*(COMMON)
*(.bss)
*(.stack)
}
__bss_end = .;
__bss_size = __bss_end - __bss_start;
/* Some built-in stack space... */
. = ALIGN(0x1000);
. = . + 0x1000;
__bootstrap_stack_top = .;
end = .;
/DISCARD/ :
{
*(.comment)
*(.eh_frame)
*(.note.gnu.build-id)
}
}

View File

@ -0,0 +1,375 @@
#include <stdint.h>
#include <kernel/printf.h>
#include <kernel/string.h>
#include <kernel/elf.h>
#define MMIO_BASE 0xFE000000UL
#define MBOX_BASE (MMIO_BASE + 0xB880)
#define MBOX_READ (MBOX_BASE + 0x00)
#define MBOX_STATUS (MBOX_BASE + 0x18)
#define MBOX_WRITE (MBOX_BASE + 0x20)
#define MBOX_FULL 0x80000000
#define MBOX_EMPTY 0x40000000
#define MBOX_RESPONSE 0x80000000
#define MBOX_REQUEST 0
volatile uint32_t __attribute__((aligned(16))) mbox[36];
static uint32_t mmio_read32(uintptr_t addr) {
uint32_t res = *((volatile uint32_t*)(addr));
return res;
}
static void mmio_write32(uintptr_t addr, uint32_t val) {
(*((volatile uint32_t*)(addr))) = val;
}
uint32_t mbox_call(uint8_t ch) {
uint32_t r = ((uint32_t)((uintptr_t)&mbox) & ~0xF) | (ch & 0xF);
while (mmio_read32(MBOX_STATUS) == MBOX_FULL); /* wait for mailbox to be ready */
mmio_write32(MBOX_WRITE, r);
while (1) {
while (mmio_read32(MBOX_STATUS) & MBOX_EMPTY);
if (r == mmio_read32(MBOX_READ)) return mbox[1] == MBOX_RESPONSE;
}
}
uint8_t * lfb_vid_memory = 0;
uint16_t lfb_resolution_x = 0;
uint16_t lfb_resolution_y = 0;
uint16_t lfb_resolution_b = 0;
uint32_t lfb_resolution_s = 0;
size_t lfb_memsize = 0;
void * malloc(size_t x) {
while (1);
}
int rpi_fb_init(void) {
int i = 0;
#define MB(j) mbox[i++] = j
MB(35 * 4);
MB(MBOX_REQUEST);
MB(0x48003);
MB(8);
MB(0);
int fb_width = i;
MB(1920);
int fb_height = i;
MB(1080);
MB(0x48004);
MB(8);
MB(8);
MB(1920);
MB(1080);
MB(0x48009);
MB(8);
MB(8);
MB(0);
MB(0);
MB(0x48005);
MB(4);
MB(4);
int fb_bpp = i;
MB(32);
MB(0x48006);
MB(4);
MB(4);
MB(1);
MB(0x40001);
MB(8);
MB(8);
int fb_pointer = i;
MB(4096);
int fb_size = i;
MB(0);
MB(0x40008);
MB(4);
MB(4);
int fb_pitch = i;
MB(0);
MB(0);
if (mbox_call(8) && mbox[fb_bpp] == 32 && mbox[fb_pointer] != 0) {
lfb_vid_memory = (uint8_t*)(uintptr_t)(mbox[fb_pointer] & 0x3FFFFFFF);
lfb_resolution_x = mbox[fb_width];
lfb_resolution_y = mbox[fb_height];
lfb_resolution_s = mbox[fb_pitch];
lfb_resolution_b = mbox[fb_bpp];
lfb_memsize = mbox[fb_size];
for (unsigned int y = 0; y < lfb_resolution_y; ++y) {
for (unsigned int x = 0; x < lfb_resolution_x; ++x) {
*(volatile uint32_t *)((uintptr_t)lfb_vid_memory + y * lfb_resolution_s + x * 4) = 0x3ea3f0;
}
}
extern void fbterm_initialize(void);
fbterm_initialize();
return 0;
}
return 1;
}
extern char _kernel_start[];
extern char _kernel_end[];
extern char _ramdisk_start[];
extern char _ramdisk_end[];
static struct BaseTables {
uintptr_t l0_base[512];
uintptr_t l1_high_gbs[512];
uintptr_t l1_low_gbs[512];
uintptr_t l2_kernel[512];
} _baseTables __attribute__((aligned(4096)));
#define PTE_VALID (1UL << 0)
#define PTE_TABLE (1UL << 1)
/* Table attributes */
#define PTE_NSTABLE (1UL << 63)
#define PTE_APTABLE (3UL << 61) /* two bits */
#define PTE_APTABLE_A (1UL << 62)
#define PTE_APTABLE_B (1UL << 61)
#define PTE_UXNTABLE (1UL << 60)
#define PTE_PXNTABLE (1UL << 59)
/* Block attributes */
#define PTE_UXN (1UL << 54)
#define PTE_PXN (1UL << 53)
#define PTE_CONTIGUOUS (1UL << 52)
#define PTE_NG (1UL << 11)
#define PTE_AF (1UL << 10)
#define PTE_SH (3UL << 8) /* two bits */
#define PTE_SH_A (1UL << 9)
#define PTE_SH_B (1UL << 8)
#define PTE_AP (3UL << 6) /* two bits */
#define PTE_AP_A (1UL << 7)
#define PTE_AP_B (1UL << 6)
#define PTE_NS (1UL << 5)
#define PTE_ATTRINDX (7UL << 2) /* three bits */
#define PTE_ATTR_A (1UL << 4)
#define PTE_ATTR_B (1UL << 3)
#define PTE_ATTR_C (1UL << 2)
#define KERNEL_PHYS_BASE 0x2000000UL
static void bootstub_mmu_init(void) {
/* Map memory */
_baseTables.l0_base[0] = (uintptr_t)&_baseTables.l1_low_gbs | PTE_VALID | PTE_TABLE | PTE_AF;
/* equivalent to high_base_pml */
_baseTables.l0_base[511] = (uintptr_t)&_baseTables.l1_high_gbs | PTE_VALID | PTE_TABLE | PTE_AF;
/* Mapping for us */
_baseTables.l1_low_gbs[0] = 0x00000000UL | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
_baseTables.l1_low_gbs[1] = 0x40000000UL | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
_baseTables.l1_low_gbs[2] = 0x80000000UL | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
_baseTables.l1_low_gbs[3] = 0xc0000000UL | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
/* -512GB is a map of 64GB of memory */
for (size_t i = 0; i < 64; ++i) {
_baseTables.l1_high_gbs[i] = (i << 30) | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
}
/* -2GiB, map kernel here */
_baseTables.l1_high_gbs[510] = (uintptr_t)&_baseTables.l2_kernel | PTE_VALID | PTE_TABLE | PTE_AF;
for (size_t i = 0; i < 512; ++i) {
_baseTables.l2_kernel[i] = (KERNEL_PHYS_BASE + (i << 21)) | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
}
uint64_t sctlr = 0
| (1UL << 0) /* mmu enabled */
| (1UL << 2) /* cachability */
//| (1UL << 6)
| (1UL << 12) /* instruction cachability */
| (1UL << 23) /* SPAN */
| (1UL << 28) /* nTLSMD */
| (1UL << 29) /* LSMAOE */
| (1UL << 20) /* TSCXT */
| (1UL << 7) /* ITD */
;
/* Translate control register */
uint64_t tcr = 0
| (3UL << 32) /* 36 bits? */
| (2UL << 30) /* TG1 4KB granules in TTBR1 */
| (16UL << 16) /* T1SZ 48-bit */
| (3UL << 28) /* SH1 */
| (1UL << 26) /* ORGN1 */
| (1UL << 24) /* IRGN1 */
| (0UL << 14) /* TG0 4KB granules in TTBR0 */
| (16UL << 0) /* T0SZ 48-bit */
| (3UL << 12) /* SH0 */
| (1UL << 10) /* ORGN0 */
| (1UL << 8) /* IRGN0 */
;
/* MAIR setup? */
uint64_t mair = (0x000000000044ff00);
asm volatile ("msr MAIR_EL1,%0" :: "r"(mair));
/* Frob bits */
printf("bootstub: setting base values\n");
asm volatile ("msr TCR_EL1,%0" : : "r"(tcr));
asm volatile ("msr TTBR0_EL1,%0" : : "r"(&_baseTables.l0_base));
asm volatile ("msr TTBR1_EL1,%0" : : "r"(&_baseTables.l0_base));
printf("bootstub: frobbing bits\n");
asm volatile ("dsb ishst\ntlbi vmalle1is\ndsb ish\nisb" ::: "memory");
printf("bootstub: enabling mmu\n");
asm volatile ("msr SCTLR_EL1,%0" : : "r"(sctlr));
asm volatile ("isb" ::: "memory");
printf("bootstub: MMU initialized\n");
}
static void bootstub_load_kernel(Elf64_Header * header) {
/* Find load headers */
for (int i = 0; i < header->e_phnum; ++i) {
Elf64_Phdr * phdr = (void*)((uintptr_t)header + (header->e_phoff + header->e_phentsize * i));
if (phdr->p_type == PT_LOAD) {
printf("bootstub: Load %zu bytes @ %zx from off %zx\n", phdr->p_memsz, phdr->p_vaddr, phdr->p_offset);
memset((void*)phdr->p_vaddr, 0, phdr->p_memsz);
memcpy((void*)phdr->p_vaddr, (void*)((uintptr_t)header + phdr->p_offset), phdr->p_filesz);
} else {
printf("bootstub: Skip phdr %d\n", i);
}
}
}
struct {
uint32_t phys_addr;
uint32_t x;
uint32_t y;
uint32_t s;
uint32_t b;
uint32_t size;
uint32_t ramdisk_start;
uint32_t ramdisk_end;
} tag_data = {0};
static void bootstub_start_kernel(uintptr_t dtb, Elf64_Header * header) {
printf("bootstub: Jump to kernel entry point at %zx\n",
header->e_entry);
void (*entry)(uintptr_t,uintptr_t,uintptr_t) = (void(*)(uintptr_t,uintptr_t,uintptr_t))header->e_entry;
entry(dtb, KERNEL_PHYS_BASE, (uintptr_t)&tag_data);
}
static void bootstub_exit_el2(void) {
uint64_t spsr_el2, sctlr_el1;
asm volatile ("mrs %0, SPSR_EL2\n" :"=r"(spsr_el2));
printf("bootstub: SPSR_EL2=%#zx\n", spsr_el2);
asm volatile ("mrs %0, SCTLR_EL1\n" :"=r"(sctlr_el1));
printf("bootstub: SCTLR_EL1=%#zx\n", sctlr_el1);
/* get us out of EL2 */
asm volatile (
"ldr x0, =0x1004\n"
"mrs x1, SCTLR_EL2\n"
"orr x1, x1, x0\n"
"msr SCTLR_EL2, x1\n"
"ldr x0, =0x30d01804\n"
"msr SCTLR_EL1, x0\n" ::: "x0", "x1");
printf("bootstub: sctlr_el1 set\n");
asm volatile (
"ldr x0, =0x80000000\n"
"msr HCR_EL2, x0\n" ::: "x0");
printf("bootstub: hcr set\n");
#if 0
asm volatile (
"ldr x0, =0x431\n"
"msr SCR_EL3, x0\n" ::: "x0");
printf("bootstub: SCR_EL3 set\n");
#endif
asm volatile (
"ldr x0, =0x3c5\n"
"msr SPSR_EL2, x0\n" ::: "x0");
printf("bootstub: spsr_el2 set\n");
asm volatile (
"mov x0, sp\n"
"msr SP_EL1, x0\n"
"adr x0, in_el1\n"
"msr ELR_EL2, x0\n"
"eret\n"
"in_el1:\n"
::: "x0", "memory", "cc"
);
printf("bootstub: out of EL2?\n");
uint64_t CurrentEL;
asm volatile ("mrs %0, CurrentEL" : "=r"(CurrentEL));
printf("in el%zu\n", CurrentEL >> 2);
}
void kmain(uint32_t dtb_address, uint32_t base_addr) {
if (rpi_fb_init()) {
/* Panic */
while (1);
}
printf("rpi4 bootstub, kernel base address is %#x, dtb is at %#x\n", base_addr, dtb_address);
printf("framebuffer (%u x %u) @ %#zx\n",
lfb_resolution_x,
lfb_resolution_y,
(uintptr_t)lfb_vid_memory);
uint64_t CurrentEL;
asm volatile ("mrs %0, CurrentEL" : "=r"(CurrentEL));
printf("in el%zu\n", CurrentEL >> 2);
printf("kernel @ %#zx (%zu bytes) ramdisk @ %#zx (%zu bytes)\n",
(uintptr_t)&_kernel_start,
(size_t)((uintptr_t)&_kernel_end - (uintptr_t)&_kernel_start),
(uintptr_t)&_ramdisk_start,
(size_t)((uintptr_t)&_ramdisk_end - (uintptr_t)&_ramdisk_start));
bootstub_exit_el2();
bootstub_mmu_init();
tag_data.phys_addr = lfb_vid_memory;
tag_data.x = lfb_resolution_x;
tag_data.y = lfb_resolution_y;
tag_data.s = lfb_resolution_s;
tag_data.b = lfb_resolution_b;
tag_data.size = lfb_memsize;
tag_data.ramdisk_start = (uintptr_t)&_ramdisk_start;
tag_data.ramdisk_end = (uintptr_t)&_ramdisk_end;
Elf64_Header *header = (void*)&_kernel_start;
bootstub_load_kernel(header);
/* Jump to kernel */
bootstub_start_kernel(dtb_address, header);
while (1);
}

View File

@ -0,0 +1,36 @@
.extern __bootstrap_stack_top
.extern __bss_start
.extern __bss_size
.section ".bootstrap"
.globl start
start:
ldr x30, =__bootstrap_stack_top
mov x1, x4
mov sp, x30
ldr x5, =__bss_start
ldr w6, =__bss_size
3:
cbz w6, 4f
str xzr, [x5], #8
sub w6, w6, #1
cbnz w6, 3b
4:
bl kmain
hang:
b hang
.section ".rodata"
.align 12
.globl _kernel_start
_kernel_start:
.incbin "misaka-kernel"
.globl _kernel_end
_kernel_end:
.align 12
.globl _ramdisk_start
_ramdisk_start:
.incbin "ramdisk.igz"
.global _ramdisk_end
_ramdisk_end:

View File

@ -54,6 +54,7 @@ extern uint32_t lfb_resolution_s;
static inline void set_point(int x, int y, uint32_t value) {
if (lfb_resolution_b == 32) {
((uint32_t*)lfb_vid_memory)[y * (lfb_resolution_s/4) + x] = value;
asm volatile ("dc cvac, %0\n" :: "r"((uintptr_t)&((uint32_t*)lfb_vid_memory)[y * (lfb_resolution_s/4) + x]) : "memory");
} else if (lfb_resolution_b == 24) {
lfb_vid_memory[y * lfb_resolution_s + x * 3 + 0] = (value >> 0) & 0xFF;
lfb_vid_memory[y * lfb_resolution_s + x * 3 + 1] = (value >> 8) & 0xFF;
@ -295,6 +296,7 @@ size_t fbterm_write(size_t size, uint8_t *buffer) {
for (unsigned int i = 0; i < size; ++i) {
process_char(buffer[i]);
}
if (previous_writer) previous_writer(size,buffer);
return size;
}