[arch][m68k] initial port to m68k

Uses the QEMU virt machine for 68k defined in qemu 6.0+.
Basic support that boots, prints to the console, takes input from
console, and context switches.

TODO: interrupt support, timer support.
This commit is contained in:
Travis Geiselbrecht
2021-06-06 19:38:45 -07:00
parent 893b894dd5
commit 12fee4b59a
23 changed files with 1162 additions and 0 deletions

39
arch/m68k/arch.c Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/trace.h>
#include <lk/debug.h>
#include <stdint.h>
#include <arch/m68k.h>
#define LOCAL_TRACE 0
void arch_early_init(void) {
LTRACE;
}
void arch_init(void) {
LTRACE;
}
void arch_idle(void) {
// asm volatile("sleep");
}
void arch_chain_load(void *entry, ulong arg0, ulong arg1, ulong arg2, ulong arg3) {
PANIC_UNIMPLEMENTED;
}
/* unimplemented cache operations */
void arch_disable_cache(uint flags) { PANIC_UNIMPLEMENTED; }
void arch_enable_cache(uint flags) { PANIC_UNIMPLEMENTED; }
void arch_clean_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
void arch_clean_invalidate_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
void arch_invalidate_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }
void arch_sync_cache_range(addr_t start, size_t len) { PANIC_UNIMPLEMENTED; }

28
arch/m68k/asm.S Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/asm.h>
// void m68k_context_switch(struct m68k_context_switch_frame *oldcs, struct m68k_context_switch_frame *newcs);
FUNCTION(m68k_context_switch)
movel %sp@+,%d0 // pop PC off the stack
movel %sp@,%a0 // oldcs
movel %sp@(4),%a1 // newcs
// save old state
movel %sp, %a0@(0) // oldcs.sp
movel %d0, %a0@(4) // oldcs.pc
moveml %d2-%d7/%a2-%a6, %a0@(8) // oldcs.<base of callee saved list>
// load new state
movel %a1@(0), %sp // newcs.sp
movel %a1@(4), %sp@- // newcs.pc -> stack
moveml %a1@(8), %d2-%d7/%a2-%a6 // newcs.<base of callee saved list>
// return to new PC
rts
END_FUNCTION(m68k_context_switch)

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <lk/compiler.h>
#include <lk/debug.h>
__BEGIN_CDECLS
static inline void arch_enable_ints(void) {
asm("andi #~(7 << 8), %sr");
}
static inline void arch_disable_ints(void) {
asm("ori #(7 << 8), %sr");
}
static inline bool arch_ints_disabled(void) {
uint16_t sr;
asm("move %%sr, %0" : "=r"(sr));
return (sr & (7 << 8));
}
/* use a global pointer to store the current_thread */
extern struct thread *_current_thread;
static inline struct thread *arch_get_current_thread(void) {
return _current_thread;
}
static inline void arch_set_current_thread(struct thread *t) {
_current_thread = t;
}
static inline ulong arch_cycle_count(void) { return 0; }
static inline uint arch_curr_cpu_num(void) {
return 0;
}
// TODO: see if there's a proper (or required) memory barrier on 68k
#define mb() CF
#define wmb() CF
#define rmb() CF
#define smp_mb() CF
#define smp_wmb() CF
#define smp_rmb() CF
__END_CDECLS

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <sys/types.h>
struct m68k_context_switch_frame {
uint32_t sp;
uint32_t pc;
/* callee saved */
uint32_t d2;
uint32_t d3;
uint32_t d4;
uint32_t d5;
uint32_t d6;
uint32_t d7;
uint32_t a2;
uint32_t a3;
uint32_t a4;
uint32_t a5;
uint32_t a6;
};
// NOTE: consider using 'state on stack' instead
struct arch_thread {
struct m68k_context_switch_frame cs_frame;
};
void m68k_context_switch(struct m68k_context_switch_frame *oldcs, struct m68k_context_switch_frame *newcs);

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#define PAGE_SIZE 4096
#define PAGE_SIZE_SHIFT 12
// XXX is this right?
#define CACHE_LINE 32
#define ARCH_DEFAULT_STACK_SIZE 4096

View File

@@ -0,0 +1,9 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#if 0
static inline uint32_t mb_read_msr(void) {
uint32_t temp;
__asm__ volatile(
"mfs %0, rmsr;" : "=r" (temp));
return temp;
}
static inline void mb_write_msr(uint32_t val) {
__asm__ volatile(
"mts rmsr, %0" :: "r" (val));
}
#endif

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <arch/ops.h>
#include <stdbool.h>
#if WITH_SMP
#error m68k does not support SMP
#endif
#define SPIN_LOCK_INITIAL_VALUE (0)
typedef unsigned int spin_lock_t;
typedef unsigned int spin_lock_saved_state_t;
typedef unsigned int spin_lock_save_flags_t;
static inline void arch_spin_lock(spin_lock_t *lock) {
*lock = 1;
}
static inline int arch_spin_trylock(spin_lock_t *lock) {
return 0;
}
static inline void arch_spin_unlock(spin_lock_t *lock) {
*lock = 0;
}
static inline void arch_spin_lock_init(spin_lock_t *lock) {
*lock = SPIN_LOCK_INITIAL_VALUE;
}
static inline bool arch_spin_lock_held(spin_lock_t *lock) {
return *lock != 0;
}
/* default arm flag is to just disable plain irqs */
#define ARCH_DEFAULT_SPIN_LOCK_FLAG_INTERRUPTS 0
enum {
/* private */
SPIN_LOCK_STATE_RESTORE_IRQ = 1,
};
static inline void
arch_interrupt_save(spin_lock_saved_state_t *statep, spin_lock_save_flags_t flags) {
spin_lock_saved_state_t state = 0;
if (!arch_ints_disabled()) {
state |= SPIN_LOCK_STATE_RESTORE_IRQ;
arch_disable_ints();
}
*statep = state;
}
static inline void
arch_interrupt_restore(spin_lock_saved_state_t old_state, spin_lock_save_flags_t flags) {
if (old_state & SPIN_LOCK_STATE_RESTORE_IRQ)
arch_enable_ints();
}

76
arch/m68k/linker.ld Normal file
View File

@@ -0,0 +1,76 @@
OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k")
/*
* LK linker script for single segment binaries.
*/
PHDRS
{
code PT_LOAD FLAGS(5); /* PF_R|PF_X */
rodata PT_LOAD FLAGS(4); /* PF_R */
data PT_LOAD FLAGS(6); /* PF_R|PF_W */
}
ENTRY(_start)
SECTIONS
{
. = %KERNEL_BASE% + %KERNEL_LOAD_OFFSET%;
_start = .;
/* text/read-only data */
/* set the load address to physical MEMBASE */
.text : AT(%MEMBASE% + %KERNEL_LOAD_OFFSET%) {
KEEP(*(.text.boot))
*(.text .text*)
*(.gnu.linkonce.t.*)
} :code
. = ALIGN(CONSTANT(MAXPAGESIZE));
.rodata : {
__rodata_start = .;
*(.rodata .rodata.* .gnu.linkonce.r.*)
} :rodata
/* trick to force any extra sections to be emitted here */
. = .;
. = ALIGN(CONSTANT(MAXPAGESIZE));
.data : {
__data_start = .;
*(.data .data.* .gnu.linkonce.d.*)
__ctor_list = .;
KEEP(*(.ctors .init_array))
__ctor_end = .;
__dtor_list = .;
KEEP(*(.dtors .fini_array))
__dtor_end = .;
*(.got*)
*(.dynamic)
} :data
. = ALIGN(32 / 8);
__data_end = .;
__bss_start = .;
/* uninitialized data (in same segment as writable data) */
.bss : {
/* regular bss */
*(.bss .bss.*)
*(.gnu.linkonce.b.*)
}
. = ALIGN(32 / 8);
__bss_end = .;
/* Align the end to ensure anything after the kernel ends up on its own pages */
. = ALIGN(CONSTANT(MAXPAGESIZE));
_end = .;
. = %KERNEL_BASE% + %MEMSIZE%;
_end_of_ram = .;
/* Strip unnecessary stuff */
/DISCARD/ : { *(.comment .note .eh_frame) }
}

63
arch/m68k/rules.mk Normal file
View File

@@ -0,0 +1,63 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/arch.c \
$(LOCAL_DIR)/asm.S \
$(LOCAL_DIR)/start.S \
$(LOCAL_DIR)/thread.c \
# $(LOCAL_DIR)/asm.S \
$(LOCAL_DIR)/exceptions.c \
$(LOCAL_DIR)/cache.c \
$(LOCAL_DIR)/cache-ops.S \
$(LOCAL_DIR)/ops.S \
$(LOCAL_DIR)/mmu.c \
$(LOCAL_DIR)/faults.c \
$(LOCAL_DIR)/descriptor.c
GLOBAL_DEFINES += \
SMP_MAX_CPUS=1
# set the default toolchain to microblaze elf and set a #define
ifndef TOOLCHAIN_PREFIX
TOOLCHAIN_PREFIX := m68k-elf-
endif
WITH_LINKER_GC ?= 0
ARCH_COMPILEFLAGS := -mcpu=68040
LIBGCC := $(shell $(TOOLCHAIN_PREFIX)gcc $(GLOBAL_COMPILEFLAGS) $(ARCH_COMPILEFLAGS) $(GLOBAL_COMPILEFLAGS) -print-libgcc-file-name)
$(info LIBGCC = $(LIBGCC))
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`"; \
then echo "$(2)"; else echo "$(3)"; fi ;)
ARCH_OPTFLAGS := -O2
KERNEL_BASE ?= $(MEMBASE)
KERNEL_LOAD_OFFSET ?= 0
GLOBAL_DEFINES += \
MEMBASE=$(MEMBASE) \
MEMSIZE=$(MEMSIZE)
# potentially generated files that should be cleaned out with clean make rule
GENERATED += \
$(BUILDDIR)/linker.ld
# rules for generating the linker
$(BUILDDIR)/linker.ld: $(LOCAL_DIR)/linker.ld $(wildcard arch/*.ld) linkerscript.phony
@echo generating $@
@$(MKDIR)
$(NOECHO)sed "s/%MEMBASE%/$(MEMBASE)/;s/%MEMSIZE%/$(MEMSIZE)/;s/%KERNEL_BASE%/$(KERNEL_BASE)/;s/%KERNEL_LOAD_OFFSET%/$(KERNEL_LOAD_OFFSET)/" < $< > $@.tmp
@$(call TESTANDREPLACEFILE,$@.tmp,$@)
linkerscript.phony:
.PHONY: linkerscript.phony
LINKER_SCRIPT += $(BUILDDIR)/linker.ld
include make/module.mk

33
arch/m68k/start.S Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/asm.h>
.section .text.boot
FUNCTION(_start)
// clear bss
lea __bss_start,%a0
cmpal #_end,%a0
beqs 1f
0:
clrb %a0@+
cmpal #_end,%a0
bnes 0b
1:
movel #_default_stack_top,%sp
bsr lk_main
jmp .
END_FUNCTION(_start)
.bss
.align 8
_default_stack_base:
.skip 4096
_default_stack_top:

64
arch/m68k/thread.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/debug.h>
#include <lk/trace.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <kernel/thread.h>
#include <arch/m68k.h>
#define LOCAL_TRACE 0
struct thread *_current_thread;
static void initial_thread_func(void) __NO_RETURN;
static void initial_thread_func(void) {
thread_t *ct = get_current_thread();
#if LOCAL_TRACE
LTRACEF("thread %p calling %p with arg %p\n", ct, ct->entry, ct->arg);
dump_thread(ct);
#endif
/* release the thread lock that was implicitly held across the reschedule */
spin_unlock(&thread_lock);
arch_enable_ints();
int ret = ct->entry(ct->arg);
LTRACEF("thread %p exiting with %d\n", ct, ret);
thread_exit(ret);
}
void arch_thread_initialize(thread_t *t) {
LTRACEF("t %p (%s)\n", t, t->name);
/* zero out the thread context */
memset(&t->arch.cs_frame, 0, sizeof(t->arch.cs_frame));
t->arch.cs_frame.sp = (vaddr_t)t->stack + t->stack_size;
t->arch.cs_frame.pc = (vaddr_t)&initial_thread_func;
}
void arch_context_switch(thread_t *oldthread, thread_t *newthread) {
LTRACEF("old %p (%s), new %p (%s)\n", oldthread, oldthread->name, newthread, newthread->name);
m68k_context_switch(&oldthread->arch.cs_frame, &newthread->arch.cs_frame);
}
void arch_dump_thread(thread_t *t) {
#if 0
if (t->state != THREAD_RUNNING) {
dprintf(INFO, "\tarch: ");
dprintf(INFO, "sp 0x%x\n", t->arch.cs_frame.r1);
}
#endif
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "platform_p.h"
#include <assert.h>
#include <lk/err.h>
#include <lk/debug.h>
#include <lk/reg.h>
#include <lk/trace.h>
#include <kernel/debug.h>
#include <kernel/thread.h>
#include <platform/interrupts.h>
#include <platform/virt.h>
#include <platform/timer.h>
#define LOCAL_TRACE 0
static platform_timer_callback t_callback;
static volatile uint ticks = 0;
static lk_time_t periodic_interval;
void goldfish_rtc_early_init(void) {
}
void goldfish_rtc_init(void) {
}
lk_bigtime_t current_time_hires(void) {
static lk_bigtime_t bt = 0;
return ++bt;
}
lk_time_t current_time(void) {
static lk_time_t time = 0;
return ++time;
}
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval) {
LTRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
t_callback = callback;
periodic_interval = interval;
#if 0
uint32_t ticks = periodic_interval * 1000; /* timer is running close to 1Mhz */
ASSERT(ticks <= 0xffff);
TIMREG(IEN(0)) = (1<<0); // interval interrupt
TIMREG(INTERVAL_VAL(0)) = ticks;
TIMREG(CNT_CTRL(0)) = (1<<5) | (1<<4) | (1<<1); // no wave, reset, interval mode
unmask_interrupt(TTC0_A_INT);
#endif
return NO_ERROR;
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/reg.h>
#include <lk/trace.h>
#include <lib/cbuf.h>
#include <kernel/thread.h>
#include <platform.h>
#include <platform/interrupts.h>
#include <platform/debug.h>
#include <platform/virt.h>
#include <sys/types.h>
#include "platform_p.h"
// goldfish tty
// from https://github.com/qemu/qemu/blob/master/hw/char/goldfish_tty.c
volatile unsigned int * const goldfish_tty_base = (void *)VIRT_GF_TTY_MMIO_BASE;
// registers
enum {
REG_PUT_CHAR = 0x00,
REG_BYTES_READY = 0x04,
REG_CMD = 0x08,
REG_DATA_PTR = 0x10,
REG_DATA_LEN = 0x14,
REG_DATA_PTR_HIGH = 0x18,
REG_VERSION = 0x20,
};
// commands
enum {
CMD_INT_DISABLE = 0x00,
CMD_INT_ENABLE = 0x01,
CMD_WRITE_BUFFER = 0x02,
CMD_READ_BUFFER = 0x03,
};
#define RXBUF_SIZE 128
static char uart_rx_buf_data[RXBUF_SIZE];
static cbuf_t uart_rx_buf;
static char transfer_buf[1]; // static pointer used to transfer MMIO data
#if 0
// simple 16550 driver for the emulated serial port on qemu riscv virt machine
static volatile uint8_t *const uart_base = (uint8_t *)UART0_BASE_VIRT;
static inline uint8_t uart_read_8(size_t offset) {
return uart_base[offset];
}
static inline void uart_write_8(size_t offset, uint8_t val) {
uart_base[offset] = val;
}
static enum handler_return uart_irq_handler(void *arg) {
unsigned char c;
bool resched = false;
while (uart_read_8(5) & (1<<0)) {
c = uart_read_8(0);
cbuf_write_char(&uart_rx_buf, c, false);
resched = true;
}
return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
}
#endif
static void write_reg(int reg, uint32_t val) {
goldfish_tty_base[reg / 4] = val;
}
static uint32_t read_reg(int reg) {
return goldfish_tty_base[reg / 4];
}
void goldfish_tty_early_init(void) {
// make sure irqs are disabled
write_reg(REG_CMD, CMD_INT_DISABLE);
// set up the transfer buffer for receives
write_reg(REG_DATA_PTR, (uint32_t)transfer_buf);
write_reg(REG_DATA_PTR_HIGH, 0);
write_reg(REG_DATA_LEN, sizeof(transfer_buf));
}
void goldfish_tty_init(void) {
/* finish uart init to get rx going */
cbuf_initialize_etc(&uart_rx_buf, RXBUF_SIZE, uart_rx_buf_data);
#if 0
register_int_handler(IRQ_UART0, uart_irq_handler, NULL);
uart_write_8(1, 0x1); // enable receive data available interrupt
unmask_interrupt(IRQ_UART0);
#endif
}
void uart_putc(char c) {
write_reg(REG_PUT_CHAR, c);
}
int uart_getc(char *c, bool wait) {
#if 0
return cbuf_read_char(&uart_rx_buf, c, wait);
#else
return platform_pgetc(c, false);
#endif
}
void platform_dputc(char c) {
if (c == '\n')
platform_dputc('\r');
uart_putc(c);
}
int platform_dgetc(char *c, bool wait) {
int ret = uart_getc(c, wait);
return ret;
}
/* panic-time getc/putc */
void platform_pputc(char c) {
return uart_putc(c);
}
int platform_pgetc(char *c, bool wait) {
// use a DMA read of one byte if a byte is ready
if (read_reg(REG_BYTES_READY) > 0) {
write_reg(REG_CMD, CMD_READ_BUFFER);
*c = transfer_buf[0];
return 0;
}
return -1;
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
// Top level #defines for the 68k-virt machine in qemu 6.0
//
// From qemu/hw/m68k/virt.c
/*
* 6 goldfish-pic for CPU IRQ #1 to IRQ #6
* CPU IRQ #1 -> PIC #1
* IRQ #1 to IRQ #31 -> unused
* IRQ #32 -> goldfish-tty
* CPU IRQ #2 -> PIC #2
* IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32
* CPU IRQ #3 -> PIC #3
* IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64
* CPU IRQ #4 -> PIC #4
* IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96
* CPU IRQ #5 -> PIC #5
* IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128
* CPU IRQ #6 -> PIC #6
* IRQ #1 -> goldfish-rtc
* IRQ #2 to IRQ #32 -> unused
* CPU IRQ #7 -> NMI
*/
#define NUM_IRQS (6 * 32) // PIC 1 - 6
#define PIC_IRQ_TO_LINEAR(pic, irq) (((pic) - 1) * 32 + ((irq) - 1))
#define GOLDFISH_TTY_IRQ PIC_IRQ_TO_LINEAR(1, 32) // PIC 1, irq 32
#define GOLDFISH_RTC_IRQ PIC_IRQ_TO_LINEAR(6, 1) // PIC 6, irq 1
#define PIC_IRQ_BASE(num) (8 + (num - 1) * 32)
#define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1)
//#define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], (pic_irq - 8) % 32))
#define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */
#define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */
#define VIRT_GF_PIC_NB 6
/* 2 goldfish-rtc (and timer) */
#define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */
#define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */
#define VIRT_GF_RTC_NB 2
/* 1 goldfish-tty */
#define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */
#define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */
/* 1 virt-ctrl */
#define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */
#define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */
/*
* virtio-mmio size is 0x200 bytes
* we use 4 goldfish-pic to attach them,
* we can attach 32 virtio devices / goldfish-pic
* -> we can manage 32 * 4 = 128 virtio devices
*/
#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */
#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include "platform_p.h"
#include <assert.h>
#include <lk/err.h>
#include <lk/debug.h>
#include <lk/reg.h>
#include <lk/trace.h>
#include <kernel/debug.h>
#include <kernel/thread.h>
#include <platform/interrupts.h>
#include <platform/virt.h>
#define LOCAL_TRACE 0
void pic_early_init(void) {
}
void pic_init(void) {
}
static struct int_handlers {
int_handler handler;
void *arg;
} handlers[NUM_IRQS];
status_t mask_interrupt(unsigned int vector) {
//*REG32(PLIC_ENABLE(vector, riscv_current_hart())) &= ~(1 << (vector % 32));
return NO_ERROR;
}
status_t unmask_interrupt(unsigned int vector) {
//*REG32(PLIC_ENABLE(vector, riscv_current_hart())) |= (1 << (vector % 32));
return NO_ERROR;
}
void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
LTRACEF("vector %u handler %p arg %p\n", vector, handler, arg);
DEBUG_ASSERT(vector < NUM_IRQS);
handlers[vector].handler = handler;
handlers[vector].arg = arg;
}
#if 0
// Driver for PLIC implementation for qemu riscv virt machine
#define PLIC_PRIORITY(irq) (PLIC_BASE_VIRT + 4 + 4 * (irq))
#define PLIC_PENDING(irq) (PLIC_BASE_VIRT + 0x1000 + (4 * ((irq) / 32)))
#define PLIC_ENABLE(irq, hart) (PLIC_BASE_VIRT + 0x2000 + (0x80 * PLIC_HART_IDX(hart)) + (4 * ((irq) / 32)))
#define PLIC_THRESHOLD(hart) (PLIC_BASE_VIRT + 0x200000 + (0x1000 * PLIC_HART_IDX(hart)))
#define PLIC_COMPLETE(hart) (PLIC_BASE_VIRT + 0x200004 + (0x1000 * PLIC_HART_IDX(hart)))
#define PLIC_CLAIM(hart) PLIC_COMPLETE(hart)
void plic_early_init(void) {
// mask all irqs and set their priority to 1
// TODO: mask on all the other cpus too
for (int i = 1; i < NUM_IRQS; i++) {
*REG32(PLIC_ENABLE(i, riscv_current_hart())) &= ~(1 << (i % 32));
*REG32(PLIC_PRIORITY(i)) = 1;
}
// set global priority threshold to 0
*REG32(PLIC_THRESHOLD(riscv_current_hart())) = 0;
}
void plic_init(void) {
}
status_t mask_interrupt(unsigned int vector) {
*REG32(PLIC_ENABLE(vector, riscv_current_hart())) &= ~(1 << (vector % 32));
return NO_ERROR;
}
status_t unmask_interrupt(unsigned int vector) {
*REG32(PLIC_ENABLE(vector, riscv_current_hart())) |= (1 << (vector % 32));
return NO_ERROR;
}
void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
LTRACEF("vector %u handler %p arg %p\n", vector, handler, arg);
DEBUG_ASSERT(vector < NUM_IRQS);
handlers[vector].handler = handler;
handlers[vector].arg = arg;
}
enum handler_return riscv_platform_irq(void) {
// see what irq triggered it
uint32_t vector = *REG32(PLIC_CLAIM(riscv_current_hart()));
LTRACEF("vector %u\n", vector);
if (unlikely(vector == 0)) {
// nothing pending
return INT_NO_RESCHEDULE;
}
THREAD_STATS_INC(interrupts);
KEVLOG_IRQ_ENTER(vector);
enum handler_return ret = INT_NO_RESCHEDULE;
if (handlers[vector].handler) {
ret = handlers[vector].handler(handlers[vector].arg);
}
// ack the interrupt
*REG32(PLIC_COMPLETE(riscv_current_hart())) = vector;
KEVLOG_IRQ_EXIT(vector);
return ret;
}
#endif

View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 2018 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/reg.h>
#include <lk/trace.h>
#include <kernel/thread.h>
#include <platform.h>
#include <platform/interrupts.h>
#include <platform/debug.h>
#include <platform/timer.h>
#include <platform/virt.h>
#include <sys/types.h>
#include <dev/virtio.h>
#include <dev/virtio/net.h>
#if WITH_LIB_MINIP
#include <lib/minip.h>
#endif
#if WITH_KERNEL_VM
#include <kernel/vm.h>
#else
#include <kernel/novm.h>
#endif
#if WITH_LIB_CONSOLE
#include <lib/console.h>
#endif
#include "platform_p.h"
#define LOCAL_TRACE 0
extern ulong lk_boot_args[4];
#if WITH_KERNEL_VM
#define DEFAULT_MEMORY_SIZE (MEMSIZE) /* try to fetch from the emulator via the fdt */
static pmm_arena_t arena = {
.name = "ram",
.base = MEMORY_BASE_PHYS,
.size = DEFAULT_MEMORY_SIZE,
.flags = PMM_ARENA_FLAG_KMAP,
};
#endif
void platform_early_init(void) {
goldfish_tty_early_init();
pic_early_init();
goldfish_rtc_early_init();
#if 0
plic_early_init();
LTRACEF("starting FDT scan\n");
/* look for a flattened device tree in the second arg passed to us */
bool found_mem = false;
int cpu_count = 0;
const void *fdt = (void *)lk_boot_args[1];
#if WITH_KERNEL_VM
fdt = (const void *)((uintptr_t)fdt + PERIPHERAL_BASE_VIRT);
#endif
struct fdt_walk_callbacks cb = {
.mem = memcallback,
.memcookie = &found_mem,
.cpu = cpucallback,
.cpucookie = &cpu_count,
};
status_t err = fdt_walk(fdt, &cb);
LTRACEF("fdt_walk returns %d\n", err);
if (err != 0) {
printf("FDT: error finding FDT at %p, using default memory & cpu count\n", fdt);
}
if (!found_mem) {
#if WITH_KERNEL_VM
pmm_add_arena(&arena);
#else
novm_add_arena("default", MEMBASE, MEMSIZE);
#endif
}
if (cpu_count > 0) {
printf("FDT: found %d cpus\n", cpu_count);
riscv_set_secondary_count(cpu_count - 1);
}
#if WITH_KERNEL_VM
/* reserve the first 256K of ram which is marked protected by the PMP in firmware */
struct list_node list = LIST_INITIAL_VALUE(list);
pmm_alloc_range(MEMBASE, 0x40000 / PAGE_SIZE, &list);
#endif
LTRACEF("done scanning FDT\n");
/* save a copy of the pointer to the poweroff/reset register */
/* TODO: read it from the FDT */
#if WITH_KERNEL_VM
power_reset_reg = paddr_to_kvaddr(0x100000);
#else
power_reset_reg = (void *)0x100000;
#endif
#endif
}
void platform_init(void) {
pic_init();
goldfish_tty_init();
goldfish_rtc_init();
#if 0
plic_init();
uart_init();
/* detect any virtio devices */
uint virtio_irqs[NUM_VIRTIO_TRANSPORTS];
for (int i = 0; i < NUM_VIRTIO_TRANSPORTS; i++) {
virtio_irqs[i] = IRQ_VIRTIO_BASE + i;
}
virtio_mmio_detect((void *)VIRTIO_BASE_VIRT, NUM_VIRTIO_TRANSPORTS, virtio_irqs, VIRTIO_STRIDE);
#if WITH_LIB_MINIP
if (virtio_net_found() > 0) {
uint8_t mac_addr[6];
virtio_net_get_mac_addr(mac_addr);
TRACEF("found virtio networking interface\n");
/* start minip */
minip_set_macaddr(mac_addr);
__UNUSED uint32_t ip_addr = IPV4(192, 168, 0, 99);
__UNUSED uint32_t ip_mask = IPV4(255, 255, 255, 0);
__UNUSED uint32_t ip_gateway = IPV4_NONE;
//minip_init(virtio_net_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_init_dhcp(virtio_net_send_minip_pkt, NULL);
virtio_net_start();
}
#endif
#endif
}
#if 0
void platform_halt(platform_halt_action suggested_action,
platform_halt_reason reason) {
switch (suggested_action) {
case HALT_ACTION_SHUTDOWN:
dprintf(ALWAYS, "Shutting down... (reason = %d)\n", reason);
*power_reset_reg = 0x5555;
break;
case HALT_ACTION_REBOOT:
dprintf(ALWAYS, "Rebooting... (reason = %d)\n", reason);
*power_reset_reg = 0x7777;
break;
case HALT_ACTION_HALT:
#if ENABLE_PANIC_SHELL
if (reason == HALT_REASON_SW_PANIC) {
dprintf(ALWAYS, "CRASH: starting debug shell... (reason = %d)\n", reason);
arch_disable_ints();
panic_shell_start();
}
#endif // ENABLE_PANIC_SHELL
dprintf(ALWAYS, "HALT: spinning forever... (reason = %d)\n", reason);
break;
}
arch_disable_ints();
for (;;)
arch_idle();
}
#endif

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2018 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdbool.h>
void uart_init(void);
void pic_early_init(void);
void pic_init(void);
void goldfish_rtc_early_init(void);
void goldfish_rtc_init(void);
void goldfish_tty_early_init(void);
void goldfish_tty_init(void);

View File

@@ -0,0 +1,24 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
ARCH := m68k
LK_HEAP_IMPLEMENTATION ?= dlmalloc
MODULE_DEPS += lib/cbuf
MODULE_DEPS += dev/virtio/block
MODULE_DEPS += dev/virtio/gpu
MODULE_DEPS += dev/virtio/net
MODULE_SRCS += $(LOCAL_DIR)/goldfish_rtc.c
MODULE_SRCS += $(LOCAL_DIR)/goldfish_tty.c
MODULE_SRCS += $(LOCAL_DIR)/pic.c
MODULE_SRCS += $(LOCAL_DIR)/platform.c
MEMBASE ?= 0x00000000
MEMSIZE ?= 0x08000000 # default to 128MB
# we can revert to a poll based uart spin routine
GLOBAL_DEFINES += PLATFORM_SUPPORTS_PANIC_SHELL=1
include make/module.mk

View File

@@ -0,0 +1,9 @@
# main project for qemu-riscv64
MODULES += \
app/shell
include project/virtual/test.mk
include project/virtual/fs.mk
include project/virtual/minip.mk
include project/target/qemu-virt-m68k.mk

View File

@@ -0,0 +1,2 @@
TARGET := qemu-virt-m68k

11
scripts/do-qemum68k Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
set -e
set -x
PROJECT=qemu-virt-m68k-test
$DIR/make-parallel $PROJECT
qemu-system-m68k -machine virt -cpu m68040 -kernel build-${PROJECT}/lk.elf -nographic $@

View File

@@ -0,0 +1,5 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
PLATFORM := qemu-virt-m68k