Compare commits
7 commits
f037dfa939
...
288801f902
| Author | SHA1 | Date | |
|---|---|---|---|
| 288801f902 | |||
| 308947c23d | |||
| 2fa4e98197 | |||
| 7750618796 | |||
| 1fd7088eea | |||
| e32a41a32a | |||
| 65e522c267 |
16 changed files with 589 additions and 136 deletions
10
Makefile
10
Makefile
|
|
@ -22,6 +22,8 @@ KERNEL_SOURCES_x86_64 := \
|
|||
src/x86_64/address.c \
|
||||
src/x86_64/ps2_driver.c \
|
||||
src/x86_64/hwclock.c \
|
||||
src/x86_64/jit.c \
|
||||
src/x86_64/jit_call.S \
|
||||
# end of x86_64 specific kernel sources list
|
||||
|
||||
# Architecture-agnostic kernel sources.
|
||||
|
|
@ -60,12 +62,12 @@ KERNEL_SOURCES := \
|
|||
src/app_snake.c \
|
||||
src/app_tron.c \
|
||||
src/app_paint.c \
|
||||
src/app_jitpaint.c \
|
||||
src/font.c \
|
||||
src/karlos-logo.c \
|
||||
src/tlsf.c \
|
||||
src/virtio-common.c \
|
||||
src/virtio-block.c \
|
||||
src/virtio-block-example.c \
|
||||
$(KERNEL_SOURCES_$(ARCH)) \
|
||||
# end of kernel sources list
|
||||
|
||||
|
|
@ -117,6 +119,12 @@ build/tests/test_slab: tests/test_slab.c $(TEST_SLAB_DEPS) build/tests
|
|||
@"$(CC)" $(CFLAGS) -c -o $@.o $< $(CPPFLAGS)
|
||||
@"$(CC)" -o $@ $@.o $(TEST_SLAB_DEPS)
|
||||
|
||||
TEST_JIT_DEPS=build/src/test.o build/src/x86_64/jit.o build/src/x86_64/jit_call.o
|
||||
build/tests/test_jit: tests/test_jit.c $(TEST_JIT_DEPS) build/tests
|
||||
@printf "CC %s\n" $@
|
||||
@"$(CC)" $(CFLAGS) -c -o $@.o $< $(CPPFLAGS)
|
||||
@"$(CC)" -o $@ $@.o $(TEST_JIT_DEPS)
|
||||
|
||||
build/tests:
|
||||
mkdir -p $@
|
||||
|
||||
|
|
|
|||
78
include/jit.h
Normal file
78
include/jit.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#ifndef KARLOS_JIT_H
|
||||
#define KARLOS_JIT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// jit call: arguments in R0, R1, R2, ...
|
||||
// returns value in R0
|
||||
|
||||
enum op {
|
||||
ADDI, // ra += imm
|
||||
ADD, // ra += rb
|
||||
MOV, // ra = rb
|
||||
MOVI, // ra = imm
|
||||
SLL,
|
||||
SLLI,
|
||||
SRL,
|
||||
SRLI,
|
||||
SRA,
|
||||
SRAI,
|
||||
AND,
|
||||
ANDI,
|
||||
OR,
|
||||
ORI,
|
||||
XOR,
|
||||
XORI,
|
||||
RET, // return
|
||||
LD, // ra = mem[rb + imm]
|
||||
SD, // mem[rb + imm] = ra
|
||||
LW,
|
||||
LWU,
|
||||
SW,
|
||||
// TODO other load types
|
||||
};
|
||||
|
||||
enum reg {
|
||||
R0 = 0,
|
||||
R1,
|
||||
R2,
|
||||
R3,
|
||||
R4,
|
||||
R5,
|
||||
R6,
|
||||
R7,
|
||||
// used internally, do not use
|
||||
RSCRATCH
|
||||
};
|
||||
|
||||
#define GEN_ADDI(my_ra, i) ((struct insn) { .op = ADDI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_ADD(my_ra, my_rb) ((struct insn) { .op = ADD, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_ANDI(my_ra, i) ((struct insn) { .op = ANDI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_AND(my_ra, my_rb) ((struct insn) { .op = AND, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_MOV(my_ra, my_rb) ((struct insn) { .op = MOV, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_MOVI(my_ra, i) ((struct insn) { .op = MOVI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_SLLI(my_ra, i) ((struct insn) { .op = SLLI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_SRLI(my_ra, i) ((struct insn) { .op = SRLI, .ra = my_ra, .rb = 0, .imm = i })
|
||||
#define GEN_OR(my_ra, my_rb) ((struct insn) { .op = OR, .ra = my_ra, .rb = my_rb })
|
||||
#define GEN_RET() ((struct insn) { .op = RET, .ra = 0, .rb = 0 })
|
||||
#define GEN_LD(reg, addr, i) ((struct insn) { .op = LD, .ra = reg, .rb = addr, .imm = i })
|
||||
#define GEN_LW(reg, addr, i) ((struct insn) { .op = LW, .ra = reg, .rb = addr, .imm = i })
|
||||
#define GEN_LWU(reg, addr, i) ((struct insn) { .op = LWU, .ra = reg, .rb = addr, .imm = i })
|
||||
#define GEN_SD(reg, addr, i) ((struct insn) { .op = SD, .ra = reg, .rb = addr, .imm = i })
|
||||
#define GEN_SW(reg, addr, i) ((struct insn) { .op = SW, .ra = reg, .rb = addr, .imm = i })
|
||||
|
||||
struct insn {
|
||||
enum op op;
|
||||
enum reg ra;
|
||||
enum reg rb;
|
||||
uint64_t imm;
|
||||
};
|
||||
|
||||
bool jit_compile(struct insn *insns, size_t n, uint8_t *mem);
|
||||
uint64_t jit_call(uint8_t *mem, uint64_t r0, uint64_t r1);
|
||||
|
||||
void jit_test(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -35,6 +35,7 @@ struct bdf {
|
|||
};
|
||||
|
||||
#define BDF(bus, device, function) ((struct bdf){ bus, device, function })
|
||||
uint16_t pci_bdf_pack(struct bdf bdf);
|
||||
|
||||
/* Low-level functions for working with the PCI configuration space */
|
||||
uint32_t pci_config_read_u32(struct bdf bdf, uint8_t offset);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
__attribute__ ((noreturn))
|
||||
void test_fail(const char *file, unsigned int line, const char *msg);
|
||||
void test_success(const char *file, unsigned int line, const char *msg);
|
||||
void printf(const char *restrict fmt, ...);
|
||||
|
||||
extern bool should_panic;
|
||||
extern jmp_buf should_panic_buf;
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ struct virtio_blk_context {
|
|||
struct virtq_avail *avail;
|
||||
struct virtq_used *used;
|
||||
volatile le16 *queue0_notify_location;
|
||||
volatile uint8_t *interrupt_byte;
|
||||
struct queued_op *request_head;
|
||||
};
|
||||
#define VIRTIO_BLK_NOTIFY(ctx) do { (ctx).queue0_notify_location->value = 0; } while (0)
|
||||
|
|
@ -129,10 +130,14 @@ struct blk_req {
|
|||
// Requests may be completed out of order, so `cb` may also be called out of order.
|
||||
enum blk_queue_result
|
||||
virtio_blk_queue_req(
|
||||
struct virtio_blk_context *ctx,
|
||||
unsigned dev_id,
|
||||
struct blk_req req,
|
||||
void (*cb)(struct blk_req, void*),
|
||||
void *userdata);
|
||||
void virtio_blk_on_used_notif(struct virtio_blk_context *ctx);
|
||||
|
||||
// Finds and initializes all virtio block devices.
|
||||
// Returns the number of devices initialized.
|
||||
// From here on, devices can be accessed with device id 0..<return value>.
|
||||
unsigned virtio_blk_init(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -556,6 +556,7 @@ extern struct app_template app_tron_template;
|
|||
extern struct app_template app_paint_template;
|
||||
extern struct app_template app_diskviewer_template;
|
||||
extern struct app_template app_hexer_template;
|
||||
extern struct app_template app_jitpaint_template;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
|
|
@ -588,6 +589,10 @@ struct {
|
|||
{
|
||||
"hexer",
|
||||
&app_hexer_template,
|
||||
},
|
||||
{
|
||||
"jitpaint",
|
||||
&app_jitpaint_template,
|
||||
}
|
||||
};
|
||||
#define NUM_KNOWN_APPS (sizeof(known_apps)/sizeof(known_apps[0]))
|
||||
|
|
|
|||
69
src/app_jitpaint.c
Normal file
69
src/app_jitpaint.c
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#include "color.h"
|
||||
#include "keyboard.h"
|
||||
#include "std.h"
|
||||
#include "app.h"
|
||||
#include "tlsf.h"
|
||||
#include "jit.h"
|
||||
#include "widget.h"
|
||||
|
||||
#define CANVAS_WIDTH 256
|
||||
#define CANVAS_HEIGHT 256
|
||||
|
||||
struct app_jitpaint {
|
||||
struct widget *canvas;
|
||||
};
|
||||
|
||||
struct app_creation app_jitpaint_create() {
|
||||
struct app_jitpaint *app = kern_alloc(sizeof *app);
|
||||
struct widget *canvas = widget_new_canvas(CANVAS_WIDTH, CANVAS_HEIGHT, NULL, NULL);
|
||||
app->canvas = canvas;
|
||||
return (struct app_creation){ .ptr = app, .root = canvas };
|
||||
}
|
||||
|
||||
static void app_jitpaint_init(void *self) {
|
||||
struct app_jitpaint *app = self;
|
||||
// color format: pack(r, g, b) = (r<<16) | (g<<8) | b
|
||||
struct insn insns[] = {
|
||||
GEN_SRLI(R0, 5),
|
||||
GEN_SRLI(R1, 5),
|
||||
GEN_ADD(R0, R1),
|
||||
GEN_ANDI(R0, 1),
|
||||
|
||||
GEN_MOV(R1, R0),
|
||||
GEN_SLLI(R1, 1),
|
||||
GEN_OR(R0, R1),
|
||||
|
||||
GEN_MOV(R1, R0),
|
||||
GEN_SLLI(R1, 2),
|
||||
GEN_OR(R0, R1),
|
||||
|
||||
GEN_MOV(R1, R0),
|
||||
GEN_SLLI(R1, 4),
|
||||
GEN_OR(R0, R1),
|
||||
|
||||
GEN_MOV(R1, R0),
|
||||
GEN_SLLI(R1, 8),
|
||||
GEN_OR(R0, R1),
|
||||
|
||||
GEN_MOV(R1, R0),
|
||||
GEN_SLLI(R1, 8),
|
||||
GEN_OR(R0, R1),
|
||||
|
||||
GEN_RET(),
|
||||
};
|
||||
uint8_t mem[256];
|
||||
ASSERT(jit_compile(insns, sizeof(insns)/sizeof(insns[0]), mem));
|
||||
for (unsigned y = 0; y < CANVAS_HEIGHT; y++) {
|
||||
for (unsigned x = 0; x < CANVAS_WIDTH; x++) {
|
||||
uint64_t ret = jit_call(mem, x, y);
|
||||
struct color col = COLOR_RGB((ret>>16)&0xff, (ret>>8)&0xff, ret&0xff);
|
||||
widget_canvas_draw_pixel(app->canvas, col, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct app_template app_jitpaint_template = {
|
||||
.create = app_jitpaint_create,
|
||||
.init = app_jitpaint_init,
|
||||
.destroy = kern_free,
|
||||
};
|
||||
35
src/kernel.c
35
src/kernel.c
|
|
@ -13,6 +13,7 @@
|
|||
#include "texture.h"
|
||||
#include "time.h"
|
||||
#include "tar.h"
|
||||
#include "jit.h"
|
||||
|
||||
#include "x86_64/asm.h"
|
||||
#include "x86_64/ps2_driver.h"
|
||||
|
|
@ -65,6 +66,27 @@ void _start() {
|
|||
}
|
||||
}
|
||||
|
||||
void virtio_cb(struct blk_req req, void *userdata) {
|
||||
char *type = NULL;
|
||||
switch (req.type) {
|
||||
case BLK_REQ_READ: type = "READ"; break;
|
||||
case BLK_REQ_WRITE: type = "WRITE"; break;
|
||||
case BLK_REQ_FLUSH: type = "FLUSH"; break;
|
||||
}
|
||||
if (req.type == BLK_REQ_FLUSH) {
|
||||
printlinef("%s done.", type);
|
||||
} else {
|
||||
uint8_t *p = pa_to_pointer(req.data);
|
||||
printlinef("%s done: %u sectors starting at %u", type, req.num_sectors, req.sector);
|
||||
for (int i = 0; i < req.num_sectors; i++) {
|
||||
printlinef("sector %u: %X8 %X8 %X8 %X8 ...", req.sector + i,
|
||||
p[512*i], p[512*i + 1], p[512*i + 2], p[512*i + 3]
|
||||
);
|
||||
}
|
||||
}
|
||||
ram_free(ppn_from_aligned_pa(req.data));
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
__asm__("sti");
|
||||
|
||||
|
|
@ -79,19 +101,6 @@ void main(void) {
|
|||
|
||||
__asm__ ("sti");
|
||||
|
||||
bool vbe_initialize();
|
||||
bool vbe_read_file_info_from_disk(const char *name, uint64_t *size, uint64_t *bno);
|
||||
bool success = vbe_initialize();
|
||||
if (success) {
|
||||
uint64_t size, bno;
|
||||
const char *name = "a.txt";
|
||||
ASSERT(vbe_read_file_info_from_disk(name, &size, &bno));
|
||||
printlinef("file %s: size %u bno %u", name, size, bno);
|
||||
name = "b.txt";
|
||||
ASSERT(vbe_read_file_info_from_disk(name, &size, &bno));
|
||||
printlinef("file %s: size %u bno %u", name, size, bno);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
fb_refresh();
|
||||
__asm__ ("hlt");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#define PCI_CONFIG_ADDR_PORT 0xCF8
|
||||
#define PCI_CONFIG_DATA_PORT 0xCFC
|
||||
|
||||
static uint16_t pci_bdf_pack(struct bdf bdf) {
|
||||
uint16_t pci_bdf_pack(struct bdf bdf) {
|
||||
ASSERT(bdf.device < 32 && bdf.function < 8);
|
||||
return ((uint16_t)bdf.bus << 8) | ((uint16_t)bdf.device << 3) | (uint16_t)bdf.function;
|
||||
}
|
||||
|
|
@ -238,6 +238,7 @@ msi_cap_configure(struct bdf bdf, uint8_t cap_offset, unsigned icount, uint64_t
|
|||
pci_config_write_u32(bdf, cap_offset + 0x08, addr >> 32);
|
||||
pci_config_write_u32(bdf, cap_offset + 0x0C, msg);
|
||||
} else {
|
||||
ASSERT((addr >> 32) == 0);
|
||||
pci_config_write_u32(bdf, cap_offset + 0x08, msg);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
#include "virtio-block.h"
|
||||
#include "std.h"
|
||||
#include "slab.h"
|
||||
#include "tar.h"
|
||||
|
||||
void mycb(struct blk_req req, void* userdata) {
|
||||
char *type = NULL;
|
||||
switch (req.type) {
|
||||
case BLK_REQ_READ: type = "READ"; break;
|
||||
case BLK_REQ_WRITE: type = "WRITE"; break;
|
||||
case BLK_REQ_FLUSH: type = "FLUSH"; break;
|
||||
}
|
||||
if (req.type == BLK_REQ_FLUSH) {
|
||||
printlinef("%s done.", type);
|
||||
} else {
|
||||
uint8_t *p = pa_to_pointer(req.data);
|
||||
printlinef("%s done: %u sectors starting at %u", type, req.num_sectors, req.sector);
|
||||
for (int i = 0; i < req.num_sectors; i++) {
|
||||
printlinef("sector %u: %X8 %X8 %X8 %X8 ...", req.sector + i,
|
||||
p[512*i], p[512*i + 1], p[512*i + 2], p[512*i + 3]
|
||||
);
|
||||
}
|
||||
}
|
||||
slab_cache_free(pa_to_pointer(req.data));
|
||||
}
|
||||
|
||||
struct slab_cache sector_cache;
|
||||
struct cache_entry {
|
||||
bool used;
|
||||
bool done;
|
||||
uint64_t bno;
|
||||
struct pa data;
|
||||
} re1, re2;
|
||||
|
||||
void get_block_done(struct blk_req req, void* userdata) {
|
||||
struct cache_entry *entry = userdata;
|
||||
entry->done = true;
|
||||
}
|
||||
uint8_t *get_block(uint64_t bno,void *cbdata) {
|
||||
struct virtio_blk_context *ctx = cbdata;
|
||||
struct pa data = pa_from_pointer(slab_cache_alloc(§or_cache));
|
||||
volatile struct cache_entry *entry;
|
||||
if (!re1.used) {
|
||||
entry = &re1;
|
||||
} else {
|
||||
ASSERT(!re2.used);
|
||||
entry = &re2;
|
||||
}
|
||||
entry->used = true;
|
||||
entry->done = false;
|
||||
entry->bno = bno;
|
||||
entry->data = data;
|
||||
virtio_blk_queue_req(ctx, BLK_READ(bno, 1, data), get_block_done, entry);
|
||||
|
||||
while (!entry->done) {
|
||||
virtio_blk_on_used_notif(ctx);
|
||||
}
|
||||
return pa_to_pointer(entry->data);
|
||||
}
|
||||
void put_block(uint64_t bno, void *cbdata) {
|
||||
struct cache_entry *entry;
|
||||
if (re1.used && re1.bno == bno) {
|
||||
entry = &re1;
|
||||
} else {
|
||||
ASSERT(re2.used && re2.bno == bno);
|
||||
entry = &re2;
|
||||
}
|
||||
slab_cache_free(pa_to_pointer(entry->data));
|
||||
entry->used = false;
|
||||
}
|
||||
|
||||
bool vbe_initialized = false;
|
||||
bool vbe_initialized_successful = false;
|
||||
struct virtio_blk_context vbe_ctx;
|
||||
|
||||
bool vbe_initialize() {
|
||||
slab_cache_init_with_align(§or_cache, 512, 512, NULL, NULL);
|
||||
struct virtio_dev_iter iter;
|
||||
virtio_dev_iter_init(&iter, VIRTIO_DEV_KIND_BLOCK);
|
||||
struct bdf bdf;
|
||||
if (virtio_dev_iter_next(&iter, &bdf)) {
|
||||
printlinef("found virtio block device with bdf %X8", bdf);
|
||||
struct bdf second_bdf;
|
||||
if (virtio_dev_iter_next(&iter, &second_bdf)) {
|
||||
printline("WARNING: more than one virtio block device, ignoring all but first");
|
||||
}
|
||||
virtio_blk_device_init(bdf, &vbe_ctx);
|
||||
vbe_initialized = true;
|
||||
vbe_initialized_successful = true;
|
||||
return true;
|
||||
} else {
|
||||
printline("WARNING: did not find virtio device.");
|
||||
vbe_initialized = true;
|
||||
vbe_initialized_successful = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool vbe_read_file_info_from_disk(const char *name, uint64_t *size, uint64_t *bno) {
|
||||
ASSERT(vbe_initialized && vbe_initialized_successful);
|
||||
if (!vbe_initialized) {
|
||||
bool success = vbe_initialize();
|
||||
vbe_initialized = true;
|
||||
vbe_initialized_successful = success;
|
||||
}
|
||||
if (!vbe_initialized_successful) {
|
||||
return false;
|
||||
}
|
||||
ASSERT(tar_get_file_info_custom(name, vbe_ctx.num_sectors, get_block, put_block, &vbe_ctx,
|
||||
size, bno));
|
||||
ASSERT(!re1.used && !re2.used);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
#define SUBSYSTEM "vio-blk"
|
||||
#include "kernmsg.h"
|
||||
#include "pci.h"
|
||||
#include "virtio-common.h"
|
||||
#include "virtio-block.h"
|
||||
#include "std.h"
|
||||
|
|
@ -6,21 +8,27 @@
|
|||
#include "ram.h"
|
||||
#include "slab.h"
|
||||
#include "util.h"
|
||||
#include "cpu.h"
|
||||
#include "x86_64/apic.h"
|
||||
|
||||
void virtio_blk_device_init(struct bdf bdf, struct virtio_blk_context *ctx) {
|
||||
ASSERT(virtio_dev_is_of_kind(bdf, VIRTIO_DEV_KIND_BLOCK));
|
||||
ASSERT(pci_config_read_u8(bdf, PCI_HEADER_HEADER_TYPE) == 0); // expect endpoint
|
||||
|
||||
struct virtio_cap cap_common, cap_notify, cap_device;
|
||||
struct virtio_cap cap_common, cap_notify, cap_device, cap_isr;
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_COMMON_CFG, &cap_common));
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_NOTIFY_CFG, &cap_notify));
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_DEVICE_CFG, &cap_device));
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_ISR_CFG, &cap_isr));
|
||||
struct pci_bar bar = pci_config_get_bar(bdf, cap_common.barno);
|
||||
struct pa pa = pa_from_value(bar.base_address + cap_common.offset);
|
||||
volatile struct virtio_pci_common_cfg *cfg = pa_to_pointer(pa);
|
||||
bar = pci_config_get_bar(bdf, cap_device.barno);
|
||||
pa = pa_from_value(bar.base_address + cap_device.offset);
|
||||
volatile struct virtio_blk_config *dev_cfg = pa_to_pointer(pa);
|
||||
bar = pci_config_get_bar(bdf, cap_isr.barno);
|
||||
pa = pa_from_value(bar.base_address + cap_isr.offset);
|
||||
volatile uint8_t *interrupt_byte = pa_to_pointer(pa);
|
||||
|
||||
// TODO read configuration correctly (make sure `generation` doesn't change; p.24)
|
||||
uint8_t wanted_status = 0;
|
||||
|
|
@ -120,9 +128,60 @@ void virtio_blk_device_init(struct bdf bdf, struct virtio_blk_context *ctx) {
|
|||
ctx->avail = ppn_to_pointer(queue_driver_ppn);
|
||||
ctx->used = ppn_to_pointer(queue_device_ppn);
|
||||
ctx->queue0_notify_location = queue0_notify_location;
|
||||
ctx->interrupt_byte = interrupt_byte;
|
||||
ctx->request_head = NULL;
|
||||
}
|
||||
|
||||
#define VIRTIO_BLK_MAX_DEVICES 4
|
||||
// directly adjacent to SATA; TODO change?
|
||||
#define VIRTIO_INTERRUPT_VECTOR_BASE 94
|
||||
|
||||
struct virtio_blk_context virtio_blk_devices[VIRTIO_BLK_MAX_DEVICES];
|
||||
unsigned num_devices;
|
||||
|
||||
void virtio_irq_handler(unsigned num) {
|
||||
ASSERT(num < VIRTIO_BLK_MAX_DEVICES);
|
||||
void virtio_blk_on_used_notif(struct virtio_blk_context *ctx);
|
||||
virtio_blk_on_used_notif(&virtio_blk_devices[num]);
|
||||
}
|
||||
|
||||
void virtio_irq_handler_0(void) { virtio_irq_handler(0); }
|
||||
void virtio_irq_handler_1(void) { virtio_irq_handler(1); }
|
||||
void virtio_irq_handler_2(void) { virtio_irq_handler(2); }
|
||||
void virtio_irq_handler_3(void) { virtio_irq_handler(3); }
|
||||
|
||||
unsigned virtio_blk_init(void) {
|
||||
interrupt_handler_register(VIRTIO_INTERRUPT_VECTOR_BASE + 0, virtio_irq_handler_0);
|
||||
interrupt_handler_register(VIRTIO_INTERRUPT_VECTOR_BASE + 1, virtio_irq_handler_1);
|
||||
interrupt_handler_register(VIRTIO_INTERRUPT_VECTOR_BASE + 2, virtio_irq_handler_2);
|
||||
interrupt_handler_register(VIRTIO_INTERRUPT_VECTOR_BASE + 3, virtio_irq_handler_3);
|
||||
|
||||
unsigned lapic_id = lapic_get_id();
|
||||
|
||||
num_devices = 0;
|
||||
struct virtio_dev_iter it;
|
||||
virtio_dev_iter_init(&it, VIRTIO_DEV_KIND_BLOCK);
|
||||
struct bdf bdf;
|
||||
while (virtio_dev_iter_next(&it, &bdf)) {
|
||||
if (num_devices == VIRTIO_BLK_MAX_DEVICES) break;
|
||||
|
||||
printlinef("found virito device at bdf %x", pci_bdf_pack(bdf));
|
||||
|
||||
uint8_t msi_cap_offset = pci_find_cap_by_id(bdf, PCI_CAP_ID_MSI);
|
||||
uint64_t msi_addr = MSI_MAKE_ADDRESS(lapic_id);
|
||||
uint32_t msi_data = MSI_MAKE_MESSAGE(VIRTIO_INTERRUPT_VECTOR_BASE + num_devices);
|
||||
bool msi_enabled = msi_cap_configure(bdf, msi_cap_offset, 1, msi_addr, msi_data);
|
||||
|
||||
if (msi_enabled) {
|
||||
virtio_blk_device_init(bdf, &virtio_blk_devices[num_devices]);
|
||||
num_devices++;
|
||||
} else {
|
||||
printlinef("unable to configure MSI for virtio blk device at bdf %x", pci_bdf_pack(bdf));
|
||||
}
|
||||
}
|
||||
return num_devices;
|
||||
}
|
||||
|
||||
struct queued_op {
|
||||
// used by virtio device directly; TODO aligment?
|
||||
// this way we only have to do one single allocation of this struct for a queuing request.
|
||||
|
|
@ -151,6 +210,11 @@ static struct queued_op *find_queued(struct virtio_blk_context *ctx, uint16_t de
|
|||
|
||||
void virtio_blk_on_used_notif(struct virtio_blk_context *ctx) {
|
||||
ASSERT(slab_cache_initialized);
|
||||
// read interrupt byte to clear it
|
||||
uint8_t interrupt_status = *ctx->interrupt_byte;
|
||||
if (interrupt_status & 2) TODO(); // configuration change
|
||||
// we don't check if bit 1 (interrupt) is set; simply move through our queue.
|
||||
// we will notice if there is work or not.
|
||||
while (ctx->used_idx_to_handle < LE16_TO_CPU(ctx->used->idx)) {
|
||||
// TODO overflow?
|
||||
struct virtq_used_elem *used_elem = ctx->used->ring + ctx->used_idx_to_handle;
|
||||
|
|
@ -173,7 +237,7 @@ void virtio_blk_on_used_notif(struct virtio_blk_context *ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
static void virtio_blk_init(void) {
|
||||
static void virtio_blk_init_slab(void) {
|
||||
if (!slab_cache_initialized) {
|
||||
slab_cache_init(&request_cache, sizeof(struct queued_op), NULL, NULL);
|
||||
slab_cache_initialized = true;
|
||||
|
|
@ -188,12 +252,14 @@ static void fill_request(struct virtio_blk_req *req, uint32_t type, uint64_t sec
|
|||
|
||||
enum blk_queue_result
|
||||
virtio_blk_queue_req(
|
||||
struct virtio_blk_context *ctx,
|
||||
unsigned dev_id,
|
||||
struct blk_req req,
|
||||
void (*cb)(struct blk_req, void*),
|
||||
void *userdata)
|
||||
{
|
||||
virtio_blk_init();
|
||||
virtio_blk_init_slab();
|
||||
ASSERT(dev_id < num_devices);
|
||||
struct virtio_blk_context *ctx = &virtio_blk_devices[dev_id];
|
||||
unsigned op, data_flag, num_descriptors;
|
||||
switch (req.type) {
|
||||
case BLK_REQ_READ:
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ bool virtio_dev_iter_next(struct virtio_dev_iter *it, struct bdf *bdf_out) {
|
|||
bool virtio_find_cap_of_type(struct bdf bdf, unsigned type, struct virtio_cap *cap) {
|
||||
uint8_t cap_ptr = pci_config_read_u8(bdf, PCI_HEADER_CAP_PTR);
|
||||
do {
|
||||
// for some reason you need to read this as 16-bit; 2 8-bit reads don't work
|
||||
uint8_t cap_id = pci_config_read_u8(bdf, cap_ptr + 0);
|
||||
uint8_t cap_next = pci_config_read_u8(bdf, cap_ptr + 1);
|
||||
if (cap_id != CAP_VNDR_VIRTIO) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "bootparam.h"
|
||||
#include "tlsf.h"
|
||||
#include "sata.h"
|
||||
#include "virtio-block.h"
|
||||
|
||||
#include "x86_64/cmos.h"
|
||||
#include "x86_64/asm.h"
|
||||
|
|
|
|||
150
src/x86_64/jit.c
Normal file
150
src/x86_64/jit.c
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#include "std.h"
|
||||
#include "jit.h"
|
||||
|
||||
#define RAX 0
|
||||
#define RDI 7
|
||||
#define RSI 6
|
||||
#define RDX 2
|
||||
#define RCX 1
|
||||
#define R8 8
|
||||
#define R9 9
|
||||
#define R10 10
|
||||
// used as scratch
|
||||
#define R11 11
|
||||
|
||||
int x86reg(enum reg reg) {
|
||||
switch (reg) {
|
||||
case R0: return RAX;
|
||||
case R1: return RDI;
|
||||
case R2: return RSI;
|
||||
case R3: return RDX;
|
||||
case R4: return RCX;
|
||||
case R5: return R8;
|
||||
case R6: return R9;
|
||||
case R7: return R10;
|
||||
case RSCRATCH: return R11;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#define BIGREG(i) ((i) >= 8)
|
||||
|
||||
#define OPBYTE(base, reg1, reg2) ((base) | (((reg1) >> 3) << 2) | ((reg2) >> 3))
|
||||
#define MODRM(base, reg1, reg2) ((base) | (((reg1) & 7) << 3)| ((reg2) & 7))
|
||||
|
||||
uint8_t x86code(enum op op) {
|
||||
switch (op) {
|
||||
case MOV: return 0x89;
|
||||
case ADD: return 0x01;
|
||||
case AND: return 0x21;
|
||||
case OR: return 0x09;
|
||||
case LD: case LWU: return 0x8b;
|
||||
case SD: case SW: return 0x89;
|
||||
case LW: return 0x63;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
uint8_t *jit_compile_single(struct insn *insn, uint8_t *mem) {
|
||||
unsigned x86a = x86reg(insn->ra), x86b = x86reg(insn->rb);
|
||||
struct insn help;
|
||||
switch (insn->op) {
|
||||
case MOV:
|
||||
case ADD:
|
||||
case AND:
|
||||
case OR:
|
||||
*mem++ = OPBYTE(0x48, x86b, x86a);
|
||||
*mem++ = x86code(insn->op);
|
||||
*mem++ = MODRM(0xc0, x86b, x86a);
|
||||
break;
|
||||
case ADDI:
|
||||
if ((insn->imm >> 32) == 0) {
|
||||
// there is also a smaller encoding for byte adds
|
||||
if (x86a == RAX) { // rax has special encoding for some reason
|
||||
*mem++ = 0x48;
|
||||
*mem++ = 0x05;
|
||||
} else {
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0x81;
|
||||
*mem++ = MODRM(0xc0, 0, x86a);
|
||||
}
|
||||
ASSERT(insn->imm <= 0xffffffffull); // x86: 4-byte value
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
} else {
|
||||
help = GEN_MOVI(RSCRATCH, insn->imm);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
help = GEN_ADD(insn->ra, RSCRATCH);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
}
|
||||
break;
|
||||
case ANDI:
|
||||
// This is the worst case, when the operand has to be 8 bytes big. There are way better encodings. the insn also sign-extends.
|
||||
help = GEN_MOVI(RSCRATCH, insn->imm);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
help = GEN_AND(insn->ra, RSCRATCH);
|
||||
mem = jit_compile_single(&help, mem);
|
||||
break;
|
||||
case MOVI:
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0xb8 | (x86a & 7);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
break;
|
||||
case SLLI:
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0xc1;
|
||||
*mem++ = MODRM(0xe0, 0, x86a);
|
||||
ASSERT(insn->imm < 64);
|
||||
*mem++ = insn->imm;
|
||||
break;
|
||||
case SRLI:
|
||||
*mem++ = OPBYTE(0x48, 0, x86a);
|
||||
*mem++ = 0xc1;
|
||||
*mem++ = MODRM(0xe8, 0, x86a);
|
||||
ASSERT(insn->imm < 64);
|
||||
*mem++ = insn->imm;
|
||||
break;
|
||||
case LD:
|
||||
case SD:
|
||||
case LW:
|
||||
*mem++ = OPBYTE(0x48, x86a, x86b);
|
||||
*mem++ = x86code(insn->op);
|
||||
*mem++ = MODRM(0x80, x86a, x86b);
|
||||
ASSERT(insn->imm <= 0xffffffffull); // x86: 4-byte value
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
break;
|
||||
case LWU:
|
||||
case SW:
|
||||
// there are smaller insns for offset=0
|
||||
if (BIGREG(x86a) || BIGREG(x86b)) {
|
||||
*mem++ = OPBYTE(0x40, x86a, x86b);
|
||||
}
|
||||
*mem++ = x86code(insn->op);
|
||||
*mem++ = MODRM(0x80, x86a, x86b);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
*mem++ = insn->imm & 0xff;
|
||||
insn->imm >>= 8;
|
||||
}
|
||||
break;
|
||||
case RET:
|
||||
*mem++ = 0xc3;
|
||||
break;
|
||||
default: TODO();
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
bool jit_compile(struct insn *insns, size_t n, uint8_t *mem) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
mem = jit_compile_single(insns++, mem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
17
src/x86_64/jit_call.S
Normal file
17
src/x86_64/jit_call.S
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.section .note.GNU-stack
|
||||
|
||||
.text
|
||||
|
||||
// uint64_t jit_call(uint8_t *text, uint64_t r0, uint64_t r1)
|
||||
.global jit_call
|
||||
jit_call:
|
||||
mov %rdi, %r11 // save text location
|
||||
mov %rsi, %rax // r0
|
||||
mov %rdx, %rdi // r1
|
||||
mov $0, %rsi // TODO more arguments
|
||||
mov $0, %rdx
|
||||
mov $0, %rcx
|
||||
mov $0, %r8
|
||||
mov $0, %r9
|
||||
mov $0, %r10
|
||||
jmp *%r11
|
||||
158
tests/test_jit.c
Normal file
158
tests/test_jit.c
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#include <sys/mman.h>
|
||||
#include "jit.h"
|
||||
#include "test.h"
|
||||
|
||||
uint8_t *progmem;
|
||||
|
||||
void jit_test1(void) {
|
||||
// program: f(x,y) = x + y + 5
|
||||
struct insn insns[] = {
|
||||
GEN_ADD(R0, R1),
|
||||
GEN_ADDI(R0, 5),
|
||||
GEN_RET()
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
uint64_t result = jit_call(progmem, 20, 300);
|
||||
TEST_ASSERT(result == 20 + 300 + 5);
|
||||
}
|
||||
|
||||
void jit_test2(void) {
|
||||
// program: f(long *x,y) = x[0] + x[1] + x[2] (stores current total in memory)
|
||||
struct insn insns[] = {
|
||||
GEN_LD(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_SD(R3, R0, 0),
|
||||
GEN_ADDI(R0, 8),
|
||||
|
||||
GEN_LD(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_SD(R3, R0, 0),
|
||||
GEN_ADDI(R0, 8),
|
||||
|
||||
GEN_LD(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_SD(R3, R0, 0),
|
||||
GEN_ADDI(R0, 8),
|
||||
|
||||
GEN_MOV(R0, R3),
|
||||
GEN_RET(),
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
long progdata[10] = { 15, 35, 20 };
|
||||
uint64_t result = jit_call(progmem, (uint64_t)(intptr_t)progdata, 0);
|
||||
TEST_ASSERT(result == 70);
|
||||
TEST_ASSERT(progdata[0] == 15 && progdata[1] == 50 && progdata[2] == 70);
|
||||
}
|
||||
|
||||
void jit_test_lwu(void) {
|
||||
// program: f(unsigned int *x,y) = x[0] + x[1]
|
||||
struct insn insns[] = {
|
||||
GEN_LWU(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_LWU(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_MOV(R0, R3),
|
||||
GEN_RET(),
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
unsigned int progdata[10] = { 0xffffffff, 1 };
|
||||
uint64_t result = jit_call(progmem, (uint64_t)(intptr_t)progdata, 0);
|
||||
TEST_ASSERT(result == (1ull << 32));
|
||||
}
|
||||
|
||||
void jit_test_lw(void) {
|
||||
// program: f(int *x,y) = x[0] + x[1]
|
||||
struct insn insns[] = {
|
||||
GEN_LW(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_LW(R2, R0, 0),
|
||||
GEN_ADD(R3, R2),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_MOV(R0, R3),
|
||||
GEN_RET(),
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
int progdata[10] = { 0xffffffff, 1 };
|
||||
uint64_t result = jit_call(progmem, (uint64_t)(intptr_t)progdata, 0);
|
||||
TEST_ASSERT(result == 0);
|
||||
}
|
||||
|
||||
void jit_test_sw(void) {
|
||||
// program: stores in memory 5 -> 10 -> 15
|
||||
struct insn insns[] = {
|
||||
GEN_MOVI(R1, 5),
|
||||
GEN_SW(R1, R0, 0),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_ADDI(R1, 5),
|
||||
GEN_SW(R1, R0, 0),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_ADDI(R1, 5),
|
||||
GEN_SW(R1, R0, 0),
|
||||
GEN_ADDI(R0, 4),
|
||||
|
||||
GEN_MOVI(R0, 0),
|
||||
GEN_RET(),
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
int progdata[10] = { 0 };
|
||||
uint64_t result = jit_call(progmem, (uint64_t)(intptr_t)progdata, 0);
|
||||
TEST_ASSERT(result == 0);
|
||||
TEST_ASSERT(progdata[0] == 5 && progdata[1] == 10 && progdata[2] == 15 && progdata[3] == 0);
|
||||
}
|
||||
|
||||
void jit_test_insn_addi() {
|
||||
struct insn insns[] = {
|
||||
GEN_ADDI(R0, 20),
|
||||
GEN_RET(),
|
||||
GEN_RET(),
|
||||
GEN_RET(),
|
||||
};
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
uint64_t result = jit_call(progmem, 50, 0);
|
||||
TEST_ASSERT(result == 70);
|
||||
|
||||
insns[0] = GEN_ADDI(R0, 0x12345678abcdef00ull);
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
result = jit_call(progmem, 50, 0);
|
||||
TEST_ASSERT(result == 50 + 0x12345678abcdef00ull);
|
||||
|
||||
insns[0] = GEN_MOV(R7, R0);
|
||||
insns[1] = GEN_ADDI(R7, 20);
|
||||
insns[2] = GEN_MOV(R0, R7);
|
||||
insns[3] = GEN_RET();
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
result = jit_call(progmem, 50, 0);
|
||||
TEST_ASSERT(result == 70);
|
||||
|
||||
insns[0] = GEN_MOV(R7, R0);
|
||||
insns[1] = GEN_ADDI(R7, 0x12345678abcdef00ull);
|
||||
insns[2] = GEN_MOV(R0, R7);
|
||||
insns[3] = GEN_RET();
|
||||
jit_compile(insns, sizeof(insns)/sizeof(insns[0]), progmem);
|
||||
result = jit_call(progmem, 50, 0);
|
||||
TEST_ASSERT(result == 50 + 0x12345678abcdef00ull);
|
||||
}
|
||||
|
||||
int main() {
|
||||
progmem = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
TEST_ASSERT(progmem != MAP_FAILED);
|
||||
int res = mprotect(progmem, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
TEST_ASSERT(res == 0);
|
||||
|
||||
jit_test_insn_addi();
|
||||
|
||||
jit_test1();
|
||||
jit_test2();
|
||||
jit_test_lwu();
|
||||
jit_test_lw();
|
||||
jit_test_sw();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue