2021 pre-release
This commit is contained in:
parent
11059d5b6f
commit
a94708b3b5
42 changed files with 278 additions and 227 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The AbstractMachine software is:
|
||||
|
||||
Copyright (c) 2018-2020 Yanyan Jiang and Zihao Yu
|
||||
Copyright (c) 2018-2021 Yanyan Jiang and Zihao Yu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
29
Makefile
29
Makefile
|
@ -35,6 +35,11 @@ ARCH_SPLIT = $(subst -, ,$(ARCH))
|
|||
ISA = $(word 1,$(ARCH_SPLIT))
|
||||
PLATFORM = $(word 2,$(ARCH_SPLIT))
|
||||
|
||||
### Check if there is something to build
|
||||
ifeq ($(flavor SRCS), undefined)
|
||||
$(error Nothing to build)
|
||||
endif
|
||||
|
||||
### Checks end here
|
||||
endif
|
||||
|
||||
|
@ -45,26 +50,28 @@ WORK_DIR = $(shell pwd)
|
|||
DST_DIR = $(WORK_DIR)/build/$(ARCH)
|
||||
$(shell mkdir -p $(DST_DIR))
|
||||
|
||||
### Compilation targets (image or archive)
|
||||
### Compilation targets (a binary image or archive)
|
||||
IMAGE_REL = build/$(NAME)-$(ARCH)
|
||||
IMAGE = $(abspath $(IMAGE_REL))
|
||||
ARCHIVE = $(WORK_DIR)/build/$(NAME)-$(ARCH).a
|
||||
|
||||
### Files to be linked: object files (`.o`) and libraries (`.a`)
|
||||
### Collect the files to be linked: object files (`.o`) and libraries (`.a`)
|
||||
OBJS = $(addprefix $(DST_DIR)/, $(addsuffix .o, $(basename $(SRCS))))
|
||||
LIBS := $(sort $(LIBS) am klib) # lazy evaluation ("=") causes infinite recursions
|
||||
LINKAGE = $(OBJS) \
|
||||
$(addsuffix -$(ARCH).a, $(join \
|
||||
$(addsuffix /build/, $(addprefix $(AM_HOME)/, $(LIBS))), \
|
||||
$(LIBS) \
|
||||
))
|
||||
$(LIBS) ))
|
||||
|
||||
## 3. General Compilation Flags
|
||||
|
||||
### Enable Ccache acceleration when available
|
||||
CCACHE = $(if $(shell which ccache),ccache,)
|
||||
|
||||
### (Cross) compilers, e.g., mips-linux-gnu-g++
|
||||
AS = $(CROSS_COMPILE)gcc
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CXX = $(CROSS_COMPILE)g++
|
||||
AS = $(CCACHE) $(CROSS_COMPILE)gcc
|
||||
CC = $(CCACHE) $(CROSS_COMPILE)gcc
|
||||
CXX = $(CCACHE) $(CROSS_COMPILE)g++
|
||||
LD = $(CROSS_COMPILE)ld
|
||||
OBJDUMP = $(CROSS_COMPILE)objdump
|
||||
OBJCOPY = $(CROSS_COMPILE)objcopy
|
||||
|
@ -90,6 +97,12 @@ ASFLAGS += -MMD $(INCFLAGS)
|
|||
### Paste in arch-specific configurations (e.g., from `scripts/x86_64-qemu.mk`)
|
||||
-include $(AM_HOME)/scripts/$(ARCH).mk
|
||||
|
||||
### Fall back to native gcc/binutils if there is no cross compiler
|
||||
ifeq ($(wildcard $(shell which $(CC))),)
|
||||
$(info # $(CC) not found; fall back to default gcc and binutils)
|
||||
CROSS_COMPILE :=
|
||||
endif
|
||||
|
||||
## 5. Compilation Rules
|
||||
|
||||
### Rule (compile): a single `.c` -> `.o` (gcc)
|
||||
|
@ -107,7 +120,7 @@ $(DST_DIR)/%.o: %.cpp
|
|||
@mkdir -p $(dir $@) && echo + CXX $<
|
||||
@$(CXX) -std=c++17 $(CXXFLAGS) -c -o $@ $(realpath $<)
|
||||
|
||||
### Rule (compile): a single `.S` -> `.o` (gcc, which calls as)
|
||||
### Rule (compile): a single `.S` -> `.o` (gcc, which preprocesses and calls as)
|
||||
$(DST_DIR)/%.o: %.S
|
||||
@mkdir -p $(dir $@) && echo + AS $<
|
||||
@$(AS) $(ASFLAGS) -c -o $@ $(realpath $<)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef __AMDEV_H__
|
||||
#define __AMDEV_H__
|
||||
|
||||
// **MAY SUBJECT TO CHANGE IN THE FUTURE**
|
||||
|
||||
#define AM_DEVREG(id, reg, perm, ...) \
|
||||
|
@ -67,3 +70,5 @@ struct gpu_canvas {
|
|||
struct gpu_texturedesc texture;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include "platform.h"
|
||||
|
||||
#define TIMER_HZ 100
|
||||
|
||||
#define YIELD_INSTR "0xff,0x14,0x25,0x08,0x00,0x10,0x00" // callq *0x100008
|
||||
#define YIELD_INSTR_LEN ((sizeof(YIELD_INSTR)) / 5) // sizeof() counts the '\0' byte
|
||||
#define SYSCALL_INSTR_LEN YIELD_INSTR_LEN
|
||||
|
||||
static_assert(SYSCALL_INSTR_LEN == 7);
|
||||
#define SYSCALL_INSTR_LEN 7
|
||||
|
||||
static Context* (*user_handler)(Event, Context*) = NULL;
|
||||
|
||||
|
@ -23,13 +19,20 @@ static void irq_handle(Context *c) {
|
|||
c->vm_head = thiscpu->vm_head;
|
||||
c->ksp = thiscpu->ksp;
|
||||
|
||||
if (thiscpu->ev.event == EVENT_ERROR) {
|
||||
uintptr_t rip = c->uc.uc_mcontext.gregs[REG_RIP];
|
||||
printf("Unhandle signal '%s' at rip = %p, badaddr = %p, cause = 0x%x\n",
|
||||
thiscpu->ev.msg, rip, thiscpu->ev.ref, thiscpu->ev.cause);
|
||||
assert(0);
|
||||
}
|
||||
c = user_handler(thiscpu->ev, c);
|
||||
assert(c != NULL);
|
||||
|
||||
__am_switch(c);
|
||||
|
||||
// magic call to restore context
|
||||
asm volatile("call *0x100010" : : "a" (c));
|
||||
void (*p)(Context *c) = (void *)(uintptr_t)0x100008;
|
||||
p(c);
|
||||
__am_panic_on_return();
|
||||
}
|
||||
|
||||
|
@ -55,9 +58,8 @@ static void setup_stack(uintptr_t event, ucontext_t *uc) {
|
|||
|
||||
if (trap_from_user) __am_pmem_unprotect();
|
||||
|
||||
// skip the instructions causing SIGSEGV for syscall and yield
|
||||
// skip the instructions causing SIGSEGV for syscall
|
||||
if (event == EVENT_SYSCALL) { rip += SYSCALL_INSTR_LEN; }
|
||||
else if (event == EVENT_YIELD) { rip += YIELD_INSTR_LEN; }
|
||||
uc->uc_mcontext.gregs[REG_RIP] = (uintptr_t)rip;
|
||||
|
||||
// switch to kernel stack if we were previously in user space
|
||||
|
@ -80,7 +82,7 @@ static void setup_stack(uintptr_t event, ucontext_t *uc) {
|
|||
}
|
||||
|
||||
static void iret(ucontext_t *uc) {
|
||||
Context *c = (void *)uc->uc_mcontext.gregs[REG_RAX];
|
||||
Context *c = (void *)uc->uc_mcontext.gregs[REG_RDI];
|
||||
// restore the context
|
||||
*uc = c->uc;
|
||||
thiscpu->ksp = c->ksp;
|
||||
|
@ -92,13 +94,13 @@ static void sig_handler(int sig, siginfo_t *info, void *ucontext) {
|
|||
thiscpu->ev.event = EVENT_ERROR;
|
||||
switch (sig) {
|
||||
case SIGUSR1: thiscpu->ev.event = EVENT_IRQ_IODEV; break;
|
||||
case SIGUSR2: thiscpu->ev.event = EVENT_YIELD; break;
|
||||
case SIGVTALRM: thiscpu->ev.event = EVENT_IRQ_TIMER; break;
|
||||
case SIGSEGV:
|
||||
if (info->si_code == SEGV_ACCERR) {
|
||||
switch ((uintptr_t)info->si_addr) {
|
||||
case 0x100000: thiscpu->ev.event = EVENT_SYSCALL; break;
|
||||
case 0x100008: thiscpu->ev.event = EVENT_YIELD; break;
|
||||
case 0x100010: iret(ucontext); return;
|
||||
case 0x100008: iret(ucontext); return;
|
||||
}
|
||||
}
|
||||
if (__am_in_userspace(info->si_addr)) {
|
||||
|
@ -112,15 +114,14 @@ static void sig_handler(int sig, siginfo_t *info, void *ucontext) {
|
|||
}
|
||||
thiscpu->ev.ref = (uintptr_t)info->si_addr;
|
||||
}
|
||||
|
||||
if (thiscpu->ev.event == EVENT_ERROR) {
|
||||
uintptr_t rip = ((ucontext_t *)ucontext)->uc_mcontext.gregs[REG_RIP];
|
||||
printf("Unhandle SIGSEGV at rip = %p, badaddr = %p\n", rip, info->si_addr);
|
||||
}
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
assert(thiscpu->ev.event != EVENT_ERROR);
|
||||
|
||||
if (thiscpu->ev.event == EVENT_ERROR) {
|
||||
thiscpu->ev.ref = (uintptr_t)info->si_addr;
|
||||
thiscpu->ev.cause = (uintptr_t)info->si_code;
|
||||
thiscpu->ev.msg = strsignal(sig);
|
||||
}
|
||||
setup_stack(thiscpu->ev.event, ucontext);
|
||||
}
|
||||
|
||||
|
@ -136,6 +137,8 @@ static void install_signal_handler() {
|
|||
assert(ret == 0);
|
||||
ret = sigaction(SIGUSR1, &s, NULL);
|
||||
assert(ret == 0);
|
||||
ret = sigaction(SIGUSR2, &s, NULL);
|
||||
assert(ret == 0);
|
||||
ret = sigaction(SIGSEGV, &s, NULL);
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
@ -178,7 +181,7 @@ Context* kcontext(Area kstack, void (*entry)(void *), void *arg) {
|
|||
}
|
||||
|
||||
void yield() {
|
||||
asm volatile (".byte " YIELD_INSTR);
|
||||
raise(SIGUSR2);
|
||||
}
|
||||
|
||||
bool ienabled() {
|
||||
|
|
|
@ -7,6 +7,7 @@ static bool ioe_init_done = false;
|
|||
void __am_timer_init();
|
||||
void __am_gpu_init();
|
||||
void __am_input_init();
|
||||
void __am_audio_init();
|
||||
void __am_input_config(AM_INPUT_CONFIG_T *);
|
||||
void __am_timer_config(AM_TIMER_CONFIG_T *);
|
||||
void __am_timer_rtc(AM_TIMER_RTC_T *);
|
||||
|
@ -57,6 +58,7 @@ void __am_ioe_init() {
|
|||
__am_timer_init();
|
||||
__am_gpu_init();
|
||||
__am_input_init();
|
||||
__am_audio_init();
|
||||
ioe_init_done = true;
|
||||
}
|
||||
|
||||
|
|
72
am/src/native/ioe/audio.c
Normal file
72
am/src/native/ioe/audio.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <klib.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
static int rfd = -1, wfd = -1;
|
||||
static volatile int count = 0;
|
||||
|
||||
void __am_audio_init() {
|
||||
int fds[2];
|
||||
int ret = pipe2(fds, O_NONBLOCK);
|
||||
assert(ret == 0);
|
||||
rfd = fds[0];
|
||||
wfd = fds[1];
|
||||
}
|
||||
|
||||
static void audio_play(void *userdata, uint8_t *stream, int len) {
|
||||
int nread = len;
|
||||
if (count < len) nread = count;
|
||||
int b = 0;
|
||||
while (b < nread) {
|
||||
int n = read(rfd, stream, nread);
|
||||
if (n > 0) b += n;
|
||||
}
|
||||
|
||||
count -= nread;
|
||||
if (len > nread) {
|
||||
memset(stream + nread, 0, len - nread);
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_write(uint8_t *buf, int len) {
|
||||
int nwrite = 0;
|
||||
while (nwrite < len) {
|
||||
int n = write(wfd, buf, len);
|
||||
if (n == -1) n = 0;
|
||||
count += n;
|
||||
nwrite += n;
|
||||
}
|
||||
}
|
||||
|
||||
void __am_audio_ctrl(AM_AUDIO_CTRL_T *ctrl) {
|
||||
SDL_AudioSpec s = {};
|
||||
s.freq = ctrl->freq;
|
||||
s.format = AUDIO_S16SYS;
|
||||
s.channels = ctrl->channels;
|
||||
s.samples = ctrl->samples;
|
||||
s.callback = audio_play;
|
||||
s.userdata = NULL;
|
||||
|
||||
count = 0;
|
||||
int ret = SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
if (ret == 0) {
|
||||
SDL_OpenAudio(&s, NULL);
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_audio_status(AM_AUDIO_STATUS_T *stat) {
|
||||
stat->count = count;
|
||||
}
|
||||
|
||||
void __am_audio_play(AM_AUDIO_PLAY_T *ctl) {
|
||||
int len = ctl->buf.end - ctl->buf.start;
|
||||
audio_write(ctl->buf.start, len);
|
||||
}
|
||||
|
||||
void __am_audio_config(AM_AUDIO_CONFIG_T *cfg) {
|
||||
cfg->present = true;
|
||||
cfg->bufsize = fcntl(rfd, F_GETPIPE_SZ);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include <am.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <fenv.h>
|
||||
|
||||
//#define MODE_800x600
|
||||
#ifdef MODE_800x600
|
||||
|
@ -12,35 +13,32 @@
|
|||
|
||||
#define FPS 60
|
||||
|
||||
#define RMASK 0x00ff0000
|
||||
#define GMASK 0x0000ff00
|
||||
#define BMASK 0x000000ff
|
||||
#define AMASK 0x00000000
|
||||
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
|
||||
static SDL_Texture *texture = NULL;
|
||||
static uint32_t fb[W * H] = {};
|
||||
|
||||
static inline int min(int x, int y) { return (x < y) ? x : y; }
|
||||
static SDL_Surface *surface = NULL;
|
||||
|
||||
static Uint32 texture_sync(Uint32 interval, void *param) {
|
||||
SDL_UpdateTexture(texture, NULL, fb, W * sizeof(Uint32));
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
SDL_BlitScaled(surface, NULL, SDL_GetWindowSurface(window), NULL);
|
||||
SDL_UpdateWindowSurface(window);
|
||||
return interval;
|
||||
}
|
||||
|
||||
void __am_gpu_init() {
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
||||
SDL_CreateWindowAndRenderer(
|
||||
window = SDL_CreateWindow("Native Application",
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
#ifdef MODE_800x600
|
||||
W, H,
|
||||
#else
|
||||
W * 2, H * 2,
|
||||
#endif
|
||||
0, &window, &renderer);
|
||||
SDL_SetWindowTitle(window, "Native Application");
|
||||
texture = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, W, H);
|
||||
memset(fb, 0, W * H * sizeof(uint32_t));
|
||||
SDL_WINDOW_SHOWN);
|
||||
surface = SDL_CreateRGBSurface(SDL_SWSURFACE, W, H, 32,
|
||||
RMASK, GMASK, BMASK, AMASK);
|
||||
SDL_AddTimer(1000 / FPS, texture_sync, NULL);
|
||||
}
|
||||
|
||||
|
@ -58,10 +56,11 @@ void __am_gpu_status(AM_GPU_STATUS_T *stat) {
|
|||
|
||||
void __am_gpu_fbdraw(AM_GPU_FBDRAW_T *ctl) {
|
||||
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
|
||||
uint32_t *pixels = ctl->pixels;
|
||||
int cp_bytes = sizeof(uint32_t) * min(w, W - x);
|
||||
for (int j = 0; j < h && y + j < H; j ++) {
|
||||
memcpy(&fb[(y + j) * W + x], pixels, cp_bytes);
|
||||
pixels += w;
|
||||
}
|
||||
if (w == 0 || h == 0) return;
|
||||
feclearexcept(-1);
|
||||
SDL_Surface *s = SDL_CreateRGBSurfaceFrom(ctl->pixels, w, h, 32, w * sizeof(uint32_t),
|
||||
RMASK, GMASK, BMASK, AMASK);
|
||||
SDL_Rect rect = { .x = x, .y = y };
|
||||
SDL_BlitSurface(s, NULL, surface, &rect);
|
||||
SDL_FreeSurface(s);
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
#include <am.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include "platform.h"
|
||||
|
||||
#define KEYDOWN_MASK 0x8000
|
||||
|
||||
|
@ -31,7 +30,8 @@ static int event_thread(void *args) {
|
|||
key_queue[key_r] = am_code;
|
||||
key_r = (key_r + 1) % KEY_QUEUE_LEN;
|
||||
SDL_UnlockMutex(key_queue_lock);
|
||||
kill(getpid(), SIGUSR1);
|
||||
void __am_send_kbd_intr();
|
||||
__am_send_kbd_intr();
|
||||
}
|
||||
break;
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#include <am.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static struct timeval boot_time = {};
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#include <klib.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#define SBUF_SIZE_MAX 65536
|
||||
static uint8_t sbuf [SBUF_SIZE_MAX] = {};
|
||||
static int head = 0, tail = 0;
|
||||
static volatile int count = 0;
|
||||
|
||||
static void audio_play(void *userdata, uint8_t *stream, int len) {
|
||||
int nread = len;
|
||||
if (count < len) nread = count;
|
||||
|
||||
if (nread + tail < SBUF_SIZE_MAX) {
|
||||
memcpy(stream, sbuf + tail, nread);
|
||||
tail += nread;
|
||||
} else {
|
||||
int first_cpy_len = SBUF_SIZE_MAX - tail;
|
||||
memcpy(stream, sbuf + tail, first_cpy_len);
|
||||
memcpy(stream + first_cpy_len, sbuf, nread - first_cpy_len);
|
||||
tail = nread - first_cpy_len;
|
||||
}
|
||||
count -= nread;
|
||||
if (len > nread) memset(stream + nread, 0, len - nread);
|
||||
}
|
||||
|
||||
static int audio_write(uint8_t *buf, int len) {
|
||||
int free = SBUF_SIZE_MAX - count;
|
||||
int nwrite = len;
|
||||
if (free < len) nwrite = free;
|
||||
|
||||
if (nwrite + head < SBUF_SIZE_MAX) {
|
||||
memcpy(sbuf + head, buf, nwrite);
|
||||
head += nwrite;
|
||||
} else {
|
||||
int first_cpy_len = SBUF_SIZE_MAX - head;
|
||||
memcpy(sbuf + head, buf, first_cpy_len);
|
||||
memcpy(sbuf, buf + first_cpy_len, nwrite - first_cpy_len);
|
||||
head = nwrite - first_cpy_len;
|
||||
}
|
||||
count += nwrite;
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
void __am_audio_ctrl(AM_AUDIO_CTRL_T *ctrl) {
|
||||
SDL_AudioSpec s = {};
|
||||
s.freq = ctrl->freq;
|
||||
s.format = AUDIO_S16SYS;
|
||||
s.channels = ctrl->channels;
|
||||
s.samples = ctrl->samples;
|
||||
s.callback = audio_play;
|
||||
s.userdata = NULL;
|
||||
|
||||
head = tail = 0;
|
||||
count = 0;
|
||||
int ret = SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
if (ret == 0) {
|
||||
SDL_OpenAudio(&s, NULL);
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_audio_status(AM_AUDIO_STATUS_T *stat) {
|
||||
stat->count = count;
|
||||
}
|
||||
|
||||
void __am_audio_play(AM_AUDIO_PLAY_T *ctl) {
|
||||
int len = ctl->buf.end - ctl->buf.start;
|
||||
assert(len <= SBUF_SIZE_MAX);
|
||||
while (SBUF_SIZE_MAX - count < len);
|
||||
audio_write(ctl->buf.start, len);
|
||||
}
|
||||
|
||||
void __am_audio_config(AM_AUDIO_CONFIG_T *cfg) {
|
||||
cfg->present = true;
|
||||
cfg->bufsize = SBUF_SIZE_MAX;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <sys/mman.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <dlfcn.h>
|
||||
#include <elf.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -9,18 +10,19 @@
|
|||
#define MAX_CPU 16
|
||||
#define TRAP_PAGE_START (void *)0x100000
|
||||
#define PMEM_START (void *)0x1000000 // for nanos-lite with vme disabled
|
||||
#define PMEM_SIZE (128 * 1024 * 1024)
|
||||
#define PMEM_SIZE (128 * 1024 * 1024) // 128MB
|
||||
static int pmem_fd = 0;
|
||||
static void *pmem = NULL;
|
||||
static ucontext_t uc_example = {};
|
||||
static int sys_pgsz;
|
||||
static void *(*memcpy_libc)(void *, const void *, size_t) = NULL;
|
||||
sigset_t __am_intr_sigmask = {};
|
||||
__am_cpu_t *__am_cpu_struct = NULL;
|
||||
int __am_ncpu = 0;
|
||||
int __am_pgsize;
|
||||
|
||||
static void save_context_handler(int sig, siginfo_t *info, void *ucontext) {
|
||||
memcpy(&uc_example, ucontext, sizeof(uc_example));
|
||||
memcpy_libc(&uc_example, ucontext, sizeof(uc_example));
|
||||
}
|
||||
|
||||
static void save_example_context() {
|
||||
|
@ -30,7 +32,8 @@ static void save_example_context() {
|
|||
// registers. So we save the example context during signal handling
|
||||
// to get a context with everything valid.
|
||||
struct sigaction s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
void *(*memset_libc)(void *, int, size_t) = dlsym(RTLD_NEXT, "memset");
|
||||
memset_libc(&s, 0, sizeof(s));
|
||||
s.sa_sigaction = save_context_handler;
|
||||
s.sa_flags = SA_SIGINFO;
|
||||
int ret = sigaction(SIGUSR1, &s, NULL);
|
||||
|
@ -79,6 +82,10 @@ static void init_platform() {
|
|||
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
assert(ret != (void *)-1);
|
||||
|
||||
// save the address of memcpy() in glibc, since it may be linked with klib
|
||||
memcpy_libc = dlsym(RTLD_NEXT, "memcpy");
|
||||
assert(memcpy_libc != NULL);
|
||||
|
||||
// remap writable sections as MAP_SHARED
|
||||
Elf64_Phdr *phdr = (void *)getauxval(AT_PHDR);
|
||||
int phnum = (int)getauxval(AT_PHNUM);
|
||||
|
@ -96,12 +103,15 @@ static void init_platform() {
|
|||
assert(temp_mem != (void *)-1);
|
||||
|
||||
// save data and bss sections
|
||||
memcpy(temp_mem, vaddr_align, size);
|
||||
memcpy_libc(temp_mem, vaddr_align, size);
|
||||
|
||||
// save the addresses of library functions which will be used after munamp()
|
||||
// save the address of mmap() which will be used after munamp(),
|
||||
// since calling the library functions requires accessing GOT, which will be unmapped
|
||||
void *(*volatile mmap_libc)(void *, size_t, int, int, int, off_t) = &mmap;
|
||||
void *(*volatile memcpy_libc)(void *, const void *, size_t) = &memcpy;
|
||||
void *(*mmap_libc)(void *, size_t, int, int, int, off_t) = dlsym(RTLD_NEXT, "mmap");
|
||||
assert(mmap_libc != NULL);
|
||||
// load the address of memcpy() on stack, which can still be accessed
|
||||
// after the data section is unmapped
|
||||
void *(*volatile memcpy_libc_temp)(void *, const void *, size_t) = memcpy_libc;
|
||||
|
||||
// unmap the data and bss sections
|
||||
ret2 = munmap(vaddr_align, size);
|
||||
|
@ -113,7 +123,7 @@ static void init_platform() {
|
|||
assert(ret == vaddr_align);
|
||||
|
||||
// restore the data in the sections
|
||||
memcpy_libc(vaddr_align, temp_mem, size);
|
||||
memcpy_libc_temp(vaddr_align, temp_mem, size);
|
||||
|
||||
// unmap the temporary memory
|
||||
ret2 = munmap(temp_mem, size);
|
||||
|
@ -185,17 +195,21 @@ void __am_pmem_unmap(void *va) {
|
|||
}
|
||||
|
||||
void __am_get_example_uc(Context *r) {
|
||||
memcpy(&r->uc, &uc_example, sizeof(uc_example));
|
||||
memcpy_libc(&r->uc, &uc_example, sizeof(uc_example));
|
||||
}
|
||||
|
||||
void __am_get_intr_sigmask(sigset_t *s) {
|
||||
memcpy(s, &__am_intr_sigmask, sizeof(__am_intr_sigmask));
|
||||
memcpy_libc(s, &__am_intr_sigmask, sizeof(__am_intr_sigmask));
|
||||
}
|
||||
|
||||
int __am_is_sigmask_sti(sigset_t *s) {
|
||||
return !sigismember(s, SIGVTALRM);
|
||||
}
|
||||
|
||||
void __am_send_kbd_intr() {
|
||||
kill(getpid(), SIGUSR1);
|
||||
}
|
||||
|
||||
void __am_pmem_protect() {
|
||||
// int ret = mprotect(PMEM_START, PMEM_SIZE, PROT_NONE);
|
||||
// assert(ret == 0);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <search.h>
|
||||
#include "platform.h"
|
||||
|
||||
#define USER_SPACE RANGE(0x40000000, 0xc0000000)
|
||||
|
@ -8,10 +10,17 @@ typedef struct PageMap {
|
|||
struct PageMap *next;
|
||||
int prot;
|
||||
int is_mapped;
|
||||
char key[32]; // used for hsearch_r()
|
||||
} PageMap;
|
||||
|
||||
typedef struct VMHead {
|
||||
PageMap *head;
|
||||
struct hsearch_data hash;
|
||||
int nr_page;
|
||||
} VMHead;
|
||||
|
||||
#define list_foreach(p, head) \
|
||||
for (p = ((PageMap *)(head))->next; p != NULL; p = p->next)
|
||||
for (p = (PageMap *)(head); p != NULL; p = p->next)
|
||||
|
||||
extern int __am_pgsize;
|
||||
static int vme_enable = 0;
|
||||
|
@ -27,7 +36,14 @@ bool vme_init(void* (*pgalloc_f)(int), void (*pgfree_f)(void*)) {
|
|||
|
||||
void protect(AddrSpace *as) {
|
||||
assert(as != NULL);
|
||||
as->ptr = pgalloc(__am_pgsize); // used as head of the list
|
||||
VMHead *h = pgalloc(__am_pgsize); // used as head of the list
|
||||
assert(h != NULL);
|
||||
memset(h, 0, sizeof(*h));
|
||||
int max_pg = (USER_SPACE.end - USER_SPACE.start) / __am_pgsize;
|
||||
int ret = hcreate_r(max_pg, &h->hash);
|
||||
assert(ret != 0);
|
||||
|
||||
as->ptr = h;
|
||||
as->pgsize = __am_pgsize;
|
||||
as->area = USER_SPACE;
|
||||
}
|
||||
|
@ -38,13 +54,14 @@ void unprotect(AddrSpace *as) {
|
|||
void __am_switch(Context *c) {
|
||||
if (!vme_enable) return;
|
||||
|
||||
PageMap *head = c->vm_head;
|
||||
if (head == thiscpu->vm_head) return;
|
||||
VMHead *head = c->vm_head;
|
||||
VMHead *now_head = thiscpu->vm_head;
|
||||
if (head == now_head) goto end;
|
||||
|
||||
PageMap *pp;
|
||||
if (thiscpu->vm_head != NULL) {
|
||||
if (now_head != NULL) {
|
||||
// munmap all mappings
|
||||
list_foreach(pp, thiscpu->vm_head) {
|
||||
list_foreach(pp, now_head->head) {
|
||||
if (pp->is_mapped) {
|
||||
__am_pmem_unmap(pp->va);
|
||||
pp->is_mapped = false;
|
||||
|
@ -54,13 +71,14 @@ void __am_switch(Context *c) {
|
|||
|
||||
if (head != NULL) {
|
||||
// mmap all mappings
|
||||
list_foreach(pp, head) {
|
||||
list_foreach(pp, head->head) {
|
||||
assert(IN_RANGE(pp->va, USER_SPACE));
|
||||
__am_pmem_map(pp->va, pp->pa, pp->prot);
|
||||
pp->is_mapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
thiscpu->vm_head = head;
|
||||
}
|
||||
|
||||
|
@ -70,23 +88,30 @@ void map(AddrSpace *as, void *va, void *pa, int prot) {
|
|||
assert((uintptr_t)pa % __am_pgsize == 0);
|
||||
assert(as != NULL);
|
||||
PageMap *pp = NULL;
|
||||
PageMap *vm_head = as->ptr;
|
||||
VMHead *vm_head = as->ptr;
|
||||
assert(vm_head != NULL);
|
||||
list_foreach(pp, vm_head) {
|
||||
if (pp->va == va) break;
|
||||
}
|
||||
|
||||
if (pp == NULL) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, "%x", va);
|
||||
ENTRY item = { .key = buf };
|
||||
ENTRY *item_find;
|
||||
hsearch_r(item, FIND, &item_find, &vm_head->hash);
|
||||
if (item_find == NULL) {
|
||||
pp = pgalloc(__am_pgsize); // this will waste memory, any better idea?
|
||||
snprintf(pp->key, 32, "%x", va);
|
||||
item.key = pp->key;
|
||||
item.data = pp;
|
||||
int ret = hsearch_r(item, ENTER, &item_find, &vm_head->hash);
|
||||
assert(ret != 0);
|
||||
vm_head->nr_page ++;
|
||||
} else {
|
||||
pp = item_find->data;
|
||||
}
|
||||
pp->va = va;
|
||||
pp->pa = pa;
|
||||
pp->prot = prot;
|
||||
pp->is_mapped = false;
|
||||
// add after to vm_head to keep vm_head unchanged,
|
||||
// since vm_head is a key to describe an address space
|
||||
pp->next = vm_head->next;
|
||||
vm_head->next = pp;
|
||||
pp->next = vm_head->head;
|
||||
vm_head->head = pp;
|
||||
|
||||
if (vm_head == thiscpu->vm_head) {
|
||||
// enforce the map immediately
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
_pmem_start = 0x80000000;
|
||||
|
||||
/* at $(AM_HOME)/am/src/nemu/scripts/section.ld */
|
||||
INCLUDE "section.ld"
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
static Context* (*user_handler)(Event, Context*) = NULL;
|
||||
|
||||
static inline bool get_CU0(Context *c) { return (c->status >> 28) & 0x1; }
|
||||
|
||||
Context* __am_irq_handle(Context *c) {
|
||||
if (user_handler) {
|
||||
Event ev = {0};
|
||||
|
|
|
@ -26,12 +26,12 @@ f(30) f(31)
|
|||
.set noat
|
||||
.globl __am_asm_trap
|
||||
__am_asm_trap:
|
||||
move $k0, $sp
|
||||
move $k1, $sp
|
||||
addiu $sp, $sp, -CONTEXT_SIZE
|
||||
|
||||
MAP(REGS, PUSH)
|
||||
|
||||
sw $k0, OFFSET_SP($sp)
|
||||
sw $k1, OFFSET_SP($sp)
|
||||
|
||||
mflo $t0
|
||||
mfhi $t1
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
_pmem_start = 0x80000000;
|
||||
|
||||
/* at $(AM_HOME)/am/src/nemu/scripts/section.ld */
|
||||
INCLUDE "section.ld"
|
|
@ -1,4 +0,0 @@
|
|||
_pmem_start = 0x80000000;
|
||||
|
||||
/* at $(AM_HOME)/am/src/nemu/scripts/section.ld */
|
||||
INCLUDE "section.ld"
|
|
@ -1,4 +0,0 @@
|
|||
_pmem_start = 0x0;
|
||||
|
||||
/* at $(AM_HOME)/am/src/nemu/scripts/section.ld */
|
||||
INCLUDE "section.ld"
|
|
@ -1,7 +1,10 @@
|
|||
#include <am.h>
|
||||
#include <stdatomic.h>
|
||||
#include <klib-macros.h>
|
||||
|
||||
bool mpe_init(void (*entry)()) {
|
||||
return false;
|
||||
entry();
|
||||
panic("MPE entry returns");
|
||||
}
|
||||
|
||||
int cpu_count() {
|
||||
|
@ -13,5 +16,5 @@ int cpu_current() {
|
|||
}
|
||||
|
||||
int atomic_xchg(int *addr, int newval) {
|
||||
return 0;
|
||||
return atomic_exchange(addr, newval);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
SRCS := start.S main.c
|
||||
bootblock.o: $(SRCS) Makefile
|
||||
@echo + CC $(SRCS)
|
||||
@gcc -m32 -Os -nostdlib -Ttext 0x7c00 -I$(AM_HOME)/am/src -o bootblock.o $(SRCS)
|
||||
@$(CROSS_COMPILE)gcc -static -m32 -fno-pic -Os -nostdlib -Ttext 0x7c00 -I$(AM_HOME)/am/src -o bootblock.o $(SRCS)
|
||||
@python3 genboot.py bootblock.o
|
||||
|
||||
clean:
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import sys, pathlib, subprocess
|
||||
import os, sys, pathlib, subprocess
|
||||
|
||||
f = pathlib.Path(sys.argv[1])
|
||||
try:
|
||||
objcopy = os.getenv('CROSS_COMPILE', '') + 'objcopy'
|
||||
data = subprocess.run(
|
||||
['objcopy', '-S', '-O', 'binary', '-j', '.text', f, '/dev/stdout'],
|
||||
[objcopy, '-S', '-O', 'binary', '-j', '.text', f, '/dev/stdout'],
|
||||
capture_output=True).stdout
|
||||
assert len(data) <= 510
|
||||
data += b'\0' * (510 - len(data)) + b'\x55\xaa'
|
||||
|
|
|
@ -14,7 +14,7 @@ static GateDesc32 idt[NR_IRQ];
|
|||
|
||||
IRQS(IRQHANDLE_DECL)
|
||||
void __am_irqall();
|
||||
void __amkcontext_start();
|
||||
void __am_kcontext_start();
|
||||
|
||||
void __am_irq_handle(struct trap_frame *tf) {
|
||||
Context *saved_ctx = &tf->saved_context;
|
||||
|
@ -141,13 +141,13 @@ Context* kcontext(Area kstack, void (*entry)(void *), void *arg) {
|
|||
|
||||
#if __x86_64__
|
||||
ctx->cs = KSEL(SEG_KCODE);
|
||||
ctx->rip = (uintptr_t)__amkcontext_start;
|
||||
ctx->rip = (uintptr_t)__am_kcontext_start;
|
||||
ctx->rflags = FL_IF;
|
||||
ctx->rsp = (uintptr_t)kstack.end;
|
||||
#else
|
||||
ctx->ds = KSEL(SEG_KDATA);
|
||||
ctx->cs = KSEL(SEG_KCODE);
|
||||
ctx->eip = (uintptr_t)__amkcontext_start;
|
||||
ctx->eip = (uintptr_t)__am_kcontext_start;
|
||||
ctx->eflags = FL_IF;
|
||||
ctx->esp = (uintptr_t)kstack.end;
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "x86-qemu.h"
|
||||
|
||||
.globl __amkcontext_start
|
||||
__amkcontext_start:
|
||||
.globl __am_kcontext_start
|
||||
__am_kcontext_start:
|
||||
// eax = arg, ebx = entry
|
||||
pushl %eax
|
||||
pushl $__am_panic_on_return
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "x86-qemu.h"
|
||||
|
||||
.globl __amkcontext_start
|
||||
__amkcontext_start:
|
||||
.globl __am_kcontext_start
|
||||
__am_kcontext_start:
|
||||
// rdi = arg, rsi = entry
|
||||
pushq $__am_panic_on_return
|
||||
jmpq *%rsi
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#include "x86-qemu.h"
|
||||
|
||||
Area heap = {};
|
||||
volatile uint32_t *__am_lapic;
|
||||
int __am_ncpu = 0;
|
||||
struct cpu_local __am_cpuinfo[MAX_CPU];
|
||||
|
||||
int main(const char *args);
|
||||
|
||||
|
|
|
@ -24,11 +24,12 @@ int strcmp (const char *s1, const char *s2);
|
|||
int strncmp (const char *s1, const char *s2, size_t n);
|
||||
|
||||
// stdlib.h
|
||||
int atoi (const char *nptr);
|
||||
void srand (unsigned int seed);
|
||||
int rand (void);
|
||||
void *malloc (size_t size);
|
||||
void free (void *ptr);
|
||||
int abs (int x);
|
||||
int atoi (const char *nptr);
|
||||
|
||||
// stdio.h
|
||||
int printf (const char *format, ...);
|
||||
|
@ -36,7 +37,6 @@ int sprintf (char *str, const char *format, ...);
|
|||
int snprintf (char *str, size_t size, const char *format, ...);
|
||||
int vsprintf (char *str, const char *format, va_list ap);
|
||||
int vsnprintf (char *str, size_t size, const char *format, va_list ap);
|
||||
int sscanf (const char *str, const char *format, ...);
|
||||
|
||||
// assert.h
|
||||
#ifdef NDEBUG
|
||||
|
|
|
@ -6,23 +6,23 @@
|
|||
#if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__)
|
||||
|
||||
int printf(const char *fmt, ...) {
|
||||
return 0;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int vsprintf(char *out, const char *fmt, va_list ap) {
|
||||
return 0;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int sprintf(char *out, const char *fmt, ...) {
|
||||
return 0;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int snprintf(char *out, size_t n, const char *fmt, ...) {
|
||||
return 0;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int vsnprintf(char *out, size_t n, const char *fmt, va_list ap) {
|
||||
return 0;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,7 +30,7 @@ int atoi(const char* nptr) {
|
|||
}
|
||||
|
||||
void *malloc(size_t size) {
|
||||
return NULL;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
void free(void *ptr) {
|
||||
|
|
|
@ -1,46 +1,47 @@
|
|||
#include <klib.h>
|
||||
#include <klib-macros.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__)
|
||||
|
||||
size_t strlen(const char *s) {
|
||||
return 0;
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
char *strcpy(char* dst,const char* src) {
|
||||
return NULL;
|
||||
char *strcpy(char *dst, const char *src) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
char* strncpy(char* dst, const char* src, size_t n) {
|
||||
return NULL;
|
||||
char *strncpy(char *dst, const char *src, size_t n) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
char* strcat(char* dst, const char* src) {
|
||||
return NULL;
|
||||
char *strcat(char *dst, const char *src) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int strcmp(const char* s1, const char* s2) {
|
||||
return 0;
|
||||
int strcmp(const char *s1, const char *s2) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int strncmp(const char* s1, const char* s2, size_t n) {
|
||||
return 0;
|
||||
int strncmp(const char *s1, const char *s2, size_t n) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
void* memset(void* v,int c,size_t n) {
|
||||
return NULL;
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
void* memmove(void* dst,const void* src,size_t n) {
|
||||
return NULL;
|
||||
void *memmove(void *dst, const void *src, size_t n) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
void* memcpy(void* out, const void* in, size_t n) {
|
||||
return NULL;
|
||||
void *memcpy(void *out, const void *in, size_t n) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
int memcmp(const void* s1, const void* s2, size_t n) {
|
||||
return 0;
|
||||
int memcmp(const void *s1, const void *s2, size_t n) {
|
||||
panic("Not implemented");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export CROSS_COMPILE := x86_64-linux-gnu-
|
||||
CFLAGS += -m32 -fno-pic -fno-omit-frame-pointer -march=i386
|
||||
CFLAGS += -fcf-protection=none # remove endbr32 in Ubuntu 20.04 with a CPU newer than Comet Lake
|
||||
ASFLAGS += -m32 -fno-pic
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export CROSS_COMPILE := x86_64-linux-gnu-
|
||||
CFLAGS += -m64 -fPIC -mno-sse
|
||||
ASFLAGS += -m64 -fPIC
|
||||
LDFLAGS += -melf_x86_64
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
include $(AM_HOME)/scripts/isa/mips32.mk
|
||||
include $(AM_HOME)/scripts/platform/nemu.mk
|
||||
LDFLAGS += --defsym=_pmem_start=0x80000000
|
||||
|
|
|
@ -5,20 +5,20 @@ AM_SRCS := native/trm.c \
|
|||
native/vme.c \
|
||||
native/mpe.c \
|
||||
native/platform.c \
|
||||
native/native-input.c \
|
||||
native/native-timer.c \
|
||||
native/native-gpu.c \
|
||||
native/native-audio.c \
|
||||
native/ioe/input.c \
|
||||
native/ioe/timer.c \
|
||||
native/ioe/gpu.c \
|
||||
native/ioe/audio.c \
|
||||
|
||||
CFLAGS += -fpie
|
||||
ASFLAGS += -fpie -pie
|
||||
|
||||
image:
|
||||
@echo + LD "->" $(IMAGE_REL)
|
||||
@g++ -pie -o $(IMAGE) -Wl,--whole-archive $(LINKAGE) -Wl,-no-whole-archive -lSDL2
|
||||
@g++ -pie -o $(IMAGE) -Wl,--whole-archive $(LINKAGE) -Wl,-no-whole-archive -lSDL2 -ldl
|
||||
|
||||
run: image
|
||||
$(IMAGE)
|
||||
|
||||
gdb: image
|
||||
gdb -ex "handle SIGUSR1 SIGSEGV noprint nostop" $(IMAGE)
|
||||
gdb -ex "handle SIGUSR1 SIGUSR2 SIGSEGV noprint nostop" $(IMAGE)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = _pmem_start + 0x100000;
|
||||
/* _pmem_start and _entry_offset are defined in LDFLAGS */
|
||||
. = _pmem_start + _entry_offset;
|
||||
.text : {
|
||||
*(entry)
|
||||
*(.text*)
|
|
@ -8,11 +8,10 @@ AM_SRCS := nemu/trm.c \
|
|||
nemu/isa/$(ISA)/trap.S \
|
||||
nemu/isa/$(ISA)/vme.c \
|
||||
nemu/mpe.c \
|
||||
nemu/isa/$(ISA)/boot/start.S
|
||||
nemu/isa/$(ISA)/start.S
|
||||
|
||||
CFLAGS += -fdata-sections -ffunction-sections
|
||||
LDFLAGS += -L $(AM_HOME)/am/src/nemu/scripts
|
||||
LDFLAGS += -T $(AM_HOME)/am/src/nemu/isa/$(ISA)/boot/loader.ld
|
||||
LDFLAGS += -T $(AM_HOME)/scripts/platform/nemu.ld --defsym=_entry_offset=0x100000
|
||||
LDFLAGS += --gc-sections -e _start
|
||||
NEMUFLAGS += -b -l $(shell dirname $(IMAGE).elf)/nemu-log.txt $(IMAGE).bin
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
include $(AM_HOME)/scripts/isa/riscv32.mk
|
||||
include $(AM_HOME)/scripts/platform/nemu.mk
|
||||
LDFLAGS += --defsym=_pmem_start=0x80000000
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
include $(AM_HOME)/scripts/isa/riscv64.mk
|
||||
include $(AM_HOME)/scripts/platform/nemu.mk
|
||||
LDFLAGS += --defsym=_pmem_start=0x80000000
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include $(AM_HOME)/scripts/isa/x86.mk
|
||||
CFLAGS += -mstringop-strategy=loop
|
||||
include $(AM_HOME)/scripts/platform/nemu.mk
|
||||
CFLAGS += -mstringop-strategy=loop
|
||||
LDFLAGS += --defsym=_pmem_start=0x0
|
||||
|
|
Loading…
Add table
Reference in a new issue