mirror of
https://github.com/StepanovPlaton/OSin1000Lines.git
synced 2026-04-03 20:30:46 +04:00
Compare commits
2 Commits
10c456438b
...
2c70ec98d8
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c70ec98d8 | |||
| c94866f688 |
10
src/Makefile
10
src/Makefile
@@ -1,5 +1,11 @@
|
|||||||
CC = clang
|
CC = clang
|
||||||
|
OBJCOPY=llvm-objcopy
|
||||||
CFLAGS = -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib
|
CFLAGS = -std=c11 -O2 -g3 -Wall -Wextra --target=riscv32 -ffreestanding -nostdlib
|
||||||
|
|
||||||
kernel:
|
kernel: shell
|
||||||
$(CC) $(CFLAGS) -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c common.c
|
$(CC) $(CFLAGS) -Wl,-Tkernel.ld -Wl,-Map=kernel.map -o kernel.elf kernel.c common.c shell.bin.o
|
||||||
|
|
||||||
|
shell:
|
||||||
|
$(CC) $(CFLAGS) -Wl,-Tuser.ld -Wl,-Map=shell.map -o shell.elf shell.c user.c common.c
|
||||||
|
$(OBJCOPY) --set-section-flags .bss=alloc,contents -O binary shell.elf shell.bin
|
||||||
|
$(OBJCOPY) -Ibinary -Oelf32-littleriscv shell.bin shell.bin.o
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ typedef uint32_t vaddr_t;
|
|||||||
|
|
||||||
#define PAGE_SIZE 4096
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
#define USER_BASE 0x1000000
|
||||||
|
|
||||||
|
#define SSTATUS_SPIE (1 << 5)
|
||||||
|
|
||||||
void *memset(void *buf, char c, size_t n);
|
void *memset(void *buf, char c, size_t n);
|
||||||
void *memcpy(void *dst, const void *src, size_t n);
|
void *memcpy(void *dst, const void *src, size_t n);
|
||||||
|
|
||||||
|
|||||||
171
src/kernel.c
171
src/kernel.c
@@ -54,7 +54,7 @@ void map_page(uint32_t *table1, uint32_t vaddr, paddr_t paddr, uint32_t flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===== Exception handler =====
|
// ===== Exception handler =====
|
||||||
void kernel_entry(void) {
|
__attribute__((naked)) __attribute__((aligned(4))) void kernel_entry(void) {
|
||||||
__asm__ __volatile__("csrrw sp, sscratch, sp\n"
|
__asm__ __volatile__("csrrw sp, sscratch, sp\n"
|
||||||
|
|
||||||
"addi sp, sp, -4 * 31\n"
|
"addi sp, sp, -4 * 31\n"
|
||||||
@@ -89,9 +89,11 @@ void kernel_entry(void) {
|
|||||||
"sw s10, 4 * 28(sp)\n"
|
"sw s10, 4 * 28(sp)\n"
|
||||||
"sw s11, 4 * 29(sp)\n"
|
"sw s11, 4 * 29(sp)\n"
|
||||||
|
|
||||||
|
// Извлечение и сохранение sp в момент исключения.
|
||||||
"csrr a0, sscratch\n"
|
"csrr a0, sscratch\n"
|
||||||
"sw a0, 4 * 30(sp)\n"
|
"sw a0, 4 * 30(sp)\n"
|
||||||
|
|
||||||
|
// Сброс стека ядра.
|
||||||
"addi a0, sp, 4 * 31\n"
|
"addi a0, sp, 4 * 31\n"
|
||||||
"csrw sscratch, a0\n"
|
"csrw sscratch, a0\n"
|
||||||
|
|
||||||
@@ -182,7 +184,15 @@ __attribute__((naked)) void switch_context(uint32_t *prev_sp,
|
|||||||
"ret\n");
|
"ret\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct process *create_process(uint32_t pc) {
|
__attribute__((naked)) void user_entry(void) {
|
||||||
|
__asm__ __volatile__("csrw sepc, %[sepc] \n"
|
||||||
|
"csrw sstatus, %[sstatus] \n"
|
||||||
|
"sret \n"
|
||||||
|
:
|
||||||
|
: [sepc] "r"(USER_BASE), [sstatus] "r"(SSTATUS_SPIE));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct process *create_process(const void *image, size_t image_size) {
|
||||||
struct process *proc = NULL;
|
struct process *proc = NULL;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < PROCS_MAX; i++) {
|
for (i = 0; i < PROCS_MAX; i++) {
|
||||||
@@ -208,13 +218,26 @@ struct process *create_process(uint32_t pc) {
|
|||||||
*--sp = 0; // s2
|
*--sp = 0; // s2
|
||||||
*--sp = 0; // s1
|
*--sp = 0; // s1
|
||||||
*--sp = 0; // s0
|
*--sp = 0; // s0
|
||||||
*--sp = (uint32_t)pc; // ra
|
*--sp = (uint32_t)user_entry; // ra
|
||||||
|
|
||||||
uint32_t *page_table = (uint32_t *)alloc_pages(1);
|
uint32_t *page_table = (uint32_t *)alloc_pages(1);
|
||||||
|
|
||||||
for (paddr_t paddr = (paddr_t)__kernel_base;
|
for (paddr_t paddr = (paddr_t)__kernel_base;
|
||||||
paddr < (paddr_t)__free_ram_end; paddr += PAGE_SIZE)
|
paddr < (paddr_t)__free_ram_end; paddr += PAGE_SIZE)
|
||||||
map_page(page_table, paddr, paddr, PAGE_R | PAGE_W | PAGE_X);
|
map_page(page_table, paddr, paddr, PAGE_R | PAGE_W | PAGE_X);
|
||||||
|
|
||||||
|
map_page(page_table, VIRTIO_BLK_PADDR, VIRTIO_BLK_PADDR, PAGE_R | PAGE_W);
|
||||||
|
|
||||||
|
for (uint32_t off = 0; off < image_size; off += PAGE_SIZE) {
|
||||||
|
paddr_t page = alloc_pages(1);
|
||||||
|
|
||||||
|
size_t remaining = image_size - off;
|
||||||
|
size_t copy_size = PAGE_SIZE <= remaining ? PAGE_SIZE : remaining;
|
||||||
|
|
||||||
|
memcpy((void *)page, image + off, copy_size);
|
||||||
|
map_page(page_table, USER_BASE + off, page,
|
||||||
|
PAGE_U | PAGE_R | PAGE_W | PAGE_X);
|
||||||
|
}
|
||||||
proc->pid = i + 1;
|
proc->pid = i + 1;
|
||||||
proc->state = PROC_RUNNABLE;
|
proc->state = PROC_RUNNABLE;
|
||||||
proc->sp = (uint32_t)sp;
|
proc->sp = (uint32_t)sp;
|
||||||
@@ -248,35 +271,137 @@ void yield(void) {
|
|||||||
switch_context(&prev->sp, &next->sp);
|
switch_context(&prev->sp, &next->sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Disk I/O =====
|
||||||
|
uint32_t virtio_reg_read32(unsigned offset) {
|
||||||
|
return *((volatile uint32_t *)(VIRTIO_BLK_PADDR + offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t virtio_reg_read64(unsigned offset) {
|
||||||
|
return *((volatile uint64_t *)(VIRTIO_BLK_PADDR + offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
void virtio_reg_write32(unsigned offset, uint32_t value) {
|
||||||
|
*((volatile uint32_t *)(VIRTIO_BLK_PADDR + offset)) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void virtio_reg_fetch_and_or32(unsigned offset, uint32_t value) {
|
||||||
|
virtio_reg_write32(offset, virtio_reg_read32(offset) | value);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct virtio_virtq *virtq_init(unsigned index) {
|
||||||
|
paddr_t virtq_paddr = alloc_pages(
|
||||||
|
align_up(sizeof(struct virtio_virtq), PAGE_SIZE) / PAGE_SIZE);
|
||||||
|
struct virtio_virtq *vq = (struct virtio_virtq *)virtq_paddr;
|
||||||
|
vq->queue_index = index;
|
||||||
|
vq->used_index = (volatile uint16_t *)&vq->used.index;
|
||||||
|
virtio_reg_write32(VIRTIO_REG_QUEUE_SEL, index);
|
||||||
|
virtio_reg_write32(VIRTIO_REG_QUEUE_NUM, VIRTQ_ENTRY_NUM);
|
||||||
|
virtio_reg_write32(VIRTIO_REG_QUEUE_ALIGN, 0);
|
||||||
|
virtio_reg_write32(VIRTIO_REG_QUEUE_PFN, virtq_paddr);
|
||||||
|
return vq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void virtio_blk_init(void) {
|
||||||
|
if (virtio_reg_read32(VIRTIO_REG_MAGIC) != 0x74726976)
|
||||||
|
PANIC("virtio: invalid magic value");
|
||||||
|
if (virtio_reg_read32(VIRTIO_REG_VERSION) != 1)
|
||||||
|
PANIC("virtio: invalid version");
|
||||||
|
if (virtio_reg_read32(VIRTIO_REG_DEVICE_ID) != VIRTIO_DEVICE_BLK)
|
||||||
|
PANIC("virtio: invalid device id");
|
||||||
|
|
||||||
|
virtio_reg_write32(VIRTIO_REG_DEVICE_STATUS, 0);
|
||||||
|
virtio_reg_fetch_and_or32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_ACK);
|
||||||
|
virtio_reg_fetch_and_or32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_DRIVER);
|
||||||
|
virtio_reg_fetch_and_or32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_FEAT_OK);
|
||||||
|
blk_request_vq = virtq_init(0);
|
||||||
|
virtio_reg_write32(VIRTIO_REG_DEVICE_STATUS, VIRTIO_STATUS_DRIVER_OK);
|
||||||
|
|
||||||
|
blk_capacity =
|
||||||
|
virtio_reg_read64(VIRTIO_REG_DEVICE_CONFIG + 0) * SECTOR_SIZE;
|
||||||
|
printf("virtio-blk: capacity is %d bytes\n", blk_capacity);
|
||||||
|
|
||||||
|
blk_req_paddr =
|
||||||
|
alloc_pages(align_up(sizeof(*blk_req), PAGE_SIZE) / PAGE_SIZE);
|
||||||
|
blk_req = (struct virtio_blk_req *)blk_req_paddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void virtq_kick(struct virtio_virtq *vq, int desc_index) {
|
||||||
|
vq->avail.ring[vq->avail.index % VIRTQ_ENTRY_NUM] = desc_index;
|
||||||
|
vq->avail.index++;
|
||||||
|
__sync_synchronize();
|
||||||
|
virtio_reg_write32(VIRTIO_REG_QUEUE_NOTIFY, vq->queue_index);
|
||||||
|
vq->last_used_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool virtq_is_busy(struct virtio_virtq *vq) {
|
||||||
|
return vq->last_used_index != *vq->used_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_write_disk(void *buf, unsigned sector, int is_write) {
|
||||||
|
if (sector >= blk_capacity / SECTOR_SIZE) {
|
||||||
|
printf("virtio: tried to read/write sector=%d, but capacity is %d\n",
|
||||||
|
sector, blk_capacity / SECTOR_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk_req->sector = sector;
|
||||||
|
blk_req->type = is_write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
|
||||||
|
if (is_write)
|
||||||
|
memcpy(blk_req->data, buf, SECTOR_SIZE);
|
||||||
|
|
||||||
|
struct virtio_virtq *vq = blk_request_vq;
|
||||||
|
vq->descs[0].addr = blk_req_paddr;
|
||||||
|
vq->descs[0].len = sizeof(uint32_t) * 2 + sizeof(uint64_t);
|
||||||
|
vq->descs[0].flags = VIRTQ_DESC_F_NEXT;
|
||||||
|
vq->descs[0].next = 1;
|
||||||
|
|
||||||
|
vq->descs[1].addr = blk_req_paddr + offsetof(struct virtio_blk_req, data);
|
||||||
|
vq->descs[1].len = SECTOR_SIZE;
|
||||||
|
vq->descs[1].flags =
|
||||||
|
VIRTQ_DESC_F_NEXT | (is_write ? 0 : VIRTQ_DESC_F_WRITE);
|
||||||
|
vq->descs[1].next = 2;
|
||||||
|
|
||||||
|
vq->descs[2].addr = blk_req_paddr + offsetof(struct virtio_blk_req, status);
|
||||||
|
vq->descs[2].len = sizeof(uint8_t);
|
||||||
|
vq->descs[2].flags = VIRTQ_DESC_F_WRITE;
|
||||||
|
|
||||||
|
virtq_kick(vq, 0);
|
||||||
|
|
||||||
|
while (virtq_is_busy(vq))
|
||||||
|
;
|
||||||
|
|
||||||
|
if (blk_req->status != 0) {
|
||||||
|
printf("virtio: warn: failed to read/write sector=%d status=%d\n",
|
||||||
|
sector, blk_req->status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_write)
|
||||||
|
memcpy(buf, blk_req->data, SECTOR_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
// ===== ENTRY POINT =====
|
// ===== ENTRY POINT =====
|
||||||
struct process *proc_a;
|
|
||||||
struct process *proc_b;
|
|
||||||
|
|
||||||
void proc_a_entry(void) {
|
|
||||||
while (1) {
|
|
||||||
putchar('A');
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void proc_b_entry(void) {
|
|
||||||
while (1) {
|
|
||||||
putchar('B');
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void kernel_main(void) {
|
void kernel_main(void) {
|
||||||
memset(__bss, 0, (size_t)__bss_end - (size_t)__bss);
|
memset(__bss, 0, (size_t)__bss_end - (size_t)__bss);
|
||||||
|
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
WRITE_CSR(stvec, (uint32_t)kernel_entry);
|
WRITE_CSR(stvec, (uint32_t)kernel_entry);
|
||||||
|
|
||||||
idle_proc = create_process((uint32_t)NULL);
|
virtio_blk_init();
|
||||||
|
|
||||||
|
char buf[SECTOR_SIZE];
|
||||||
|
read_write_disk(buf, 0, false);
|
||||||
|
printf("first sector: %s\n", buf);
|
||||||
|
|
||||||
|
strcpy(buf, "hello from kernel!!!\n");
|
||||||
|
read_write_disk(buf, 0, true);
|
||||||
|
|
||||||
|
idle_proc = create_process(NULL, 0);
|
||||||
idle_proc->pid = -1;
|
idle_proc->pid = -1;
|
||||||
current_proc = idle_proc;
|
current_proc = idle_proc;
|
||||||
|
|
||||||
proc_a = create_process((uint32_t)proc_a_entry);
|
create_process(_binary_shell_bin_start, (size_t)_binary_shell_bin_size);
|
||||||
proc_b = create_process((uint32_t)proc_b_entry);
|
|
||||||
|
|
||||||
yield();
|
yield();
|
||||||
PANIC("switched to idle process");
|
PANIC("switched to idle process");
|
||||||
|
|||||||
74
src/kernel.h
74
src/kernel.h
@@ -7,6 +7,7 @@ extern char __bss[], __bss_end[];
|
|||||||
extern char __stack_top[];
|
extern char __stack_top[];
|
||||||
extern char __free_ram[], __free_ram_end[];
|
extern char __free_ram[], __free_ram_end[];
|
||||||
extern char __kernel_base[];
|
extern char __kernel_base[];
|
||||||
|
extern char _binary_shell_bin_start[], _binary_shell_bin_size[];
|
||||||
|
|
||||||
// ===== SBI data ======
|
// ===== SBI data ======
|
||||||
struct sbiret {
|
struct sbiret {
|
||||||
@@ -74,6 +75,79 @@ struct process *idle_proc;
|
|||||||
#define PAGE_X (1 << 3)
|
#define PAGE_X (1 << 3)
|
||||||
#define PAGE_U (1 << 4)
|
#define PAGE_U (1 << 4)
|
||||||
|
|
||||||
|
// ===== Disk I/O =====
|
||||||
|
#define SECTOR_SIZE 512
|
||||||
|
#define VIRTQ_ENTRY_NUM 16
|
||||||
|
#define VIRTIO_DEVICE_BLK 2
|
||||||
|
#define VIRTIO_BLK_PADDR 0x10001000
|
||||||
|
#define VIRTIO_REG_MAGIC 0x00
|
||||||
|
#define VIRTIO_REG_VERSION 0x04
|
||||||
|
#define VIRTIO_REG_DEVICE_ID 0x08
|
||||||
|
#define VIRTIO_REG_QUEUE_SEL 0x30
|
||||||
|
#define VIRTIO_REG_QUEUE_NUM_MAX 0x34
|
||||||
|
#define VIRTIO_REG_QUEUE_NUM 0x38
|
||||||
|
#define VIRTIO_REG_QUEUE_ALIGN 0x3c
|
||||||
|
#define VIRTIO_REG_QUEUE_PFN 0x40
|
||||||
|
#define VIRTIO_REG_QUEUE_READY 0x44
|
||||||
|
#define VIRTIO_REG_QUEUE_NOTIFY 0x50
|
||||||
|
#define VIRTIO_REG_DEVICE_STATUS 0x70
|
||||||
|
#define VIRTIO_REG_DEVICE_CONFIG 0x100
|
||||||
|
#define VIRTIO_STATUS_ACK 1
|
||||||
|
#define VIRTIO_STATUS_DRIVER 2
|
||||||
|
#define VIRTIO_STATUS_DRIVER_OK 4
|
||||||
|
#define VIRTIO_STATUS_FEAT_OK 8
|
||||||
|
#define VIRTQ_DESC_F_NEXT 1
|
||||||
|
#define VIRTQ_DESC_F_WRITE 2
|
||||||
|
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1
|
||||||
|
#define VIRTIO_BLK_T_IN 0
|
||||||
|
#define VIRTIO_BLK_T_OUT 1
|
||||||
|
|
||||||
|
struct virtq_desc {
|
||||||
|
uint64_t addr;
|
||||||
|
uint32_t len;
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t next;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct virtq_avail {
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t index;
|
||||||
|
uint16_t ring[VIRTQ_ENTRY_NUM];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct virtq_used_elem {
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t len;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct virtq_used {
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t index;
|
||||||
|
struct virtq_used_elem ring[VIRTQ_ENTRY_NUM];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct virtio_virtq {
|
||||||
|
struct virtq_desc descs[VIRTQ_ENTRY_NUM];
|
||||||
|
struct virtq_avail avail;
|
||||||
|
struct virtq_used used __attribute__((aligned(PAGE_SIZE)));
|
||||||
|
int queue_index;
|
||||||
|
volatile uint16_t *used_index;
|
||||||
|
uint16_t last_used_index;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct virtio_blk_req {
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t reserved;
|
||||||
|
uint64_t sector;
|
||||||
|
uint8_t data[512];
|
||||||
|
uint8_t status;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct virtio_virtq *blk_request_vq;
|
||||||
|
struct virtio_blk_req *blk_req;
|
||||||
|
paddr_t blk_req_paddr;
|
||||||
|
unsigned blk_capacity;
|
||||||
|
|
||||||
// ===== Macros =====
|
// ===== Macros =====
|
||||||
#define READ_CSR(reg) \
|
#define READ_CSR(reg) \
|
||||||
({ \
|
({ \
|
||||||
|
|||||||
BIN
src/lorem.txt
Normal file
BIN
src/lorem.txt
Normal file
Binary file not shown.
@@ -1,4 +1,6 @@
|
|||||||
@echo off
|
@echo off
|
||||||
make kernel
|
make kernel
|
||||||
cd /d "C:\Program Files\qemu"
|
cd /d "C:\Program Files\qemu"
|
||||||
qemu-system-riscv32.exe -machine virt -bios default -nographic -serial mon:stdio --no-reboot -kernel %~dp0kernel.elf
|
qemu-system-riscv32.exe -machine virt -bios default -nographic -serial mon:stdio --no-reboot -drive id=drive0,file=lorem.txt,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel %~dp0kernel.elf
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -xue
|
set -xue
|
||||||
|
|
||||||
make kernel && qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot -kernel kernel.elf
|
make kernel && qemu-system-riscv32 -machine virt -bios default -nographic -serial mon:stdio --no-reboot -drive id=drive0,file=lorem.txt,format=raw,if=none -device virtio-blk-device,drive=drive0,bus=virtio-mmio-bus.0 -kernel kernel.elf
|
||||||
|
|||||||
6
src/shell.c
Normal file
6
src/shell.c
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
for (;;)
|
||||||
|
;
|
||||||
|
}
|
||||||
18
src/user.c
Normal file
18
src/user.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "user.h"
|
||||||
|
|
||||||
|
extern char __stack_top[];
|
||||||
|
|
||||||
|
__attribute__((noreturn)) void exit(void) {
|
||||||
|
for (;;)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putchar(char c) { /* Доделать*/ }
|
||||||
|
|
||||||
|
__attribute__((section(".text.start"))) __attribute__((naked)) void
|
||||||
|
start(void) {
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"mv sp, %[stack_top] \n"
|
||||||
|
"call main \n"
|
||||||
|
"call exit \n" ::[stack_top] "r"(__stack_top));
|
||||||
|
}
|
||||||
6
src/user.h
Normal file
6
src/user.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
__attribute__((noreturn)) void exit(void);
|
||||||
|
void putchar(char ch);
|
||||||
28
src/user.ld
Normal file
28
src/user.ld
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
ENTRY(start)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = 0x1000000;
|
||||||
|
|
||||||
|
.text :{
|
||||||
|
KEEP(*(.text.start));
|
||||||
|
*(.text .text.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : ALIGN(4) {
|
||||||
|
*(.rodata .rodata.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : ALIGN(4) {
|
||||||
|
*(.data .data.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : ALIGN(4) {
|
||||||
|
*(.bss .bss.* .sbss .sbss.*);
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
. += 64 * 1024; /* 64KB */
|
||||||
|
__stack_top = .;
|
||||||
|
|
||||||
|
ASSERT(. < 0x1800000, "too large executable");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user