[arch][m68k] Merge in Motorola 68k port

This commit is contained in:
Travis Geiselbrecht
2021-11-08 23:24:15 -08:00
26 changed files with 1320 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
/*
* 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>
#include <platform.h>
#define LOCAL_TRACE 0
// implementation of RTC at
// https://github.com/qemu/qemu/blob/master/hw/rtc/goldfish_rtc.c
volatile unsigned int * const goldfish_rtc_base = (void *)VIRT_GF_RTC_MMIO_BASE;
// registers
enum {
RTC_TIME_LOW = 0x00,
RTC_TIME_HIGH = 0x04,
RTC_ALARM_LOW = 0x08,
RTC_ALARM_HIGH = 0x0c,
RTC_IRQ_ENABLED = 0x10,
RTC_CLEAR_ALARM = 0x14,
RTC_ALARM_STATUS = 0x18,
RTC_CLEAR_INTERRUPT = 0x1c,
};
static uint64_t system_boot_offset;
static platform_timer_callback t_callback;
static void *t_arg;
static void write_reg(int reg, uint32_t val) {
goldfish_rtc_base[reg / 4] = val;
}
static uint32_t read_reg(int reg) {
return goldfish_rtc_base[reg / 4];
}
// raw time from the RTC is ns wall time
static uint64_t read_raw_time(void) {
uint32_t low, high;
// read both registers and assemble a 64bit counter
// reading low first latches a shadow high register which will prevent wraparound
low = read_reg(RTC_TIME_LOW);
high = read_reg(RTC_TIME_HIGH);
return ((uint64_t)high << 32) | low;
}
enum handler_return rtc_irq(void *unused) {
enum handler_return ret = INT_NO_RESCHEDULE;
write_reg(RTC_CLEAR_ALARM, 1);
write_reg(RTC_CLEAR_INTERRUPT, 1);
if (t_callback) {
ret = t_callback(t_arg, current_time());
}
return ret;
}
void goldfish_rtc_early_init(void) {
// sample the timer and use it as a offset for system start
system_boot_offset = read_raw_time();
// clear and stop any pending irqs on the timer
platform_stop_timer();
register_int_handler(GOLDFISH_RTC_IRQ, &rtc_irq, NULL);
unmask_interrupt(GOLDFISH_RTC_IRQ);
// its okay to enable the irq since we've cleared the alarm and any pending interrupts
write_reg(RTC_IRQ_ENABLED, 1);
}
void goldfish_rtc_init(void) {
}
lk_bigtime_t current_time_hires(void) {
uint64_t t = read_raw_time() - system_boot_offset;
return t / 1000ULL; // ns -> us
}
lk_time_t current_time(void) {
uint64_t t = read_raw_time() - system_boot_offset;
return (lk_time_t)(t / 1000000ULL); // ns -> ms
}
status_t platform_set_oneshot_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;
t_arg = arg;
uint64_t delta = read_raw_time();
delta += interval * 1000000ULL;
write_reg(RTC_ALARM_HIGH, delta >> 32);
write_reg(RTC_ALARM_LOW, delta & 0xffffffff);
return NO_ERROR;
}
void platform_stop_timer(void) {
LTRACE;
write_reg(RTC_CLEAR_ALARM, 1);
write_reg(RTC_CLEAR_INTERRUPT, 1);
}

View File

@@ -0,0 +1,130 @@
/*
* 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
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];
}
static enum handler_return uart_irq_handler(void *arg) {
bool resched = false;
// 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);
char c = transfer_buf[0];
cbuf_write_char(&uart_rx_buf, c, false);
resched = true;
}
return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
}
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);
register_int_handler(GOLDFISH_TTY_IRQ, uart_irq_handler, NULL);
unmask_interrupt(GOLDFISH_TTY_IRQ);
write_reg(REG_CMD, CMD_INT_ENABLE);
}
void uart_putc(char c) {
write_reg(REG_PUT_CHAR, c);
}
int uart_getc(char *c, bool wait) {
#if 1
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,67 @@
/*
* 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_PICS 6
#define NUM_IRQS (NUM_PICS * 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,130 @@
/*
* 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/bits.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
// implementation of PIC at
// https://github.com/qemu/qemu/blob/master/hw/intc/goldfish_pic.c
enum {
REG_STATUS = 0x00,
REG_IRQ_PENDING = 0x04,
REG_IRQ_DISABLE_ALL = 0x08,
REG_DISABLE = 0x0c,
REG_ENABLE = 0x10,
};
volatile unsigned int * const goldfish_pic_base = (void *)VIRT_GF_PIC_MMIO_BASE;
static struct int_handlers {
int_handler handler;
void *arg;
} handlers[NUM_IRQS];
static void write_reg(int pic, int reg, uint32_t val) {
goldfish_pic_base[0x1000 * pic / 4 + reg / 4] = val;
}
static uint32_t read_reg(int pic, int reg) {
return goldfish_pic_base[0x1000 * pic / 4 + reg / 4];
}
static void dump_pic(int i) {
dprintf(INFO, "PIC %d: status %u pending %#x\n", i, read_reg(i, REG_STATUS), read_reg(i, REG_IRQ_PENDING));
}
static void dump_all_pics(void) {
for (int i = 0; i < NUM_PICS; i++) {
dump_pic(i);
}
}
static int irq_to_pic_num(unsigned int vector) {
return vector / 32;
}
static int irq_to_pic_vec(unsigned int vector) {
return vector % 32;
}
void pic_early_init(void) {
}
void pic_init(void) {
dump_all_pics();
}
status_t mask_interrupt(unsigned int vector) {
LTRACEF("vector %u\n", vector);
write_reg(irq_to_pic_num(vector), REG_DISABLE, 1U << irq_to_pic_vec(vector));
return NO_ERROR;
}
status_t unmask_interrupt(unsigned int vector) {
LTRACEF("vector %u\n", vector);
write_reg(irq_to_pic_num(vector), REG_ENABLE, 1U << irq_to_pic_vec(vector));
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 m68k_platform_irq(uint8_t m68k_irq) {
LTRACEF("m68k irq vector %d\n", m68k_irq);
// translate m68k irqs to pic numbers
int pic_num;
if (likely(m68k_irq >= 1 && m68k_irq <= 6)) {
pic_num = m68k_irq - 1;
} else {
panic("unhandled irq %d from cpu\n", m68k_irq);
}
// see what is pending
uint32_t pending = read_reg(pic_num, REG_IRQ_PENDING);
if (pending == 0) {
// spurious
return INT_NO_RESCHEDULE;
}
// find the lowest numbered bit set
uint vector = ctz(pending) + pic_num * 32;
LTRACEF("pic %d pending %#x vector %u\n", pic_num, pending, vector);
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);
}
// no need to ack the interrupt controller since all irqs are implicitly level
KEVLOG_IRQ_EXIT(vector);
return ret;
}

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,27 @@
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
# our timer supports one shot mode
GLOBAL_DEFINES += PLATFORM_HAS_DYNAMIC_TIMER=1
include make/module.mk