Skip to content

Commit

Permalink
Use a per-cpu TLB
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Dec 25, 2017
1 parent e6f7c9c commit 255f8f1
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 148 deletions.
5 changes: 3 additions & 2 deletions emu/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
#include <softfloat.h>
#include "misc.h"
#include "emu/memory.h"
#include "emu/tlb.h"

struct cpu_state;
void cpu_run(struct cpu_state *cpu);
int cpu_step32(struct cpu_state *cpu);
int cpu_step16(struct cpu_state *cpu);
int cpu_step32(struct cpu_state *cpu, struct tlb *tlb);
int cpu_step16(struct cpu_state *cpu, struct tlb *tlb);

union xmm_reg {
qword_t qw[2];
Expand Down
6 changes: 3 additions & 3 deletions emu/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#define TRACEI(msg, ...) TRACE(msg "\t", ##__VA_ARGS__)

// this will be the next PyEval_EvalFrameEx.
int CONCAT(decoder_name, OP_SIZE)(struct cpu_state *cpu) {
int CONCAT(decoder_name, OP_SIZE)(struct cpu_state *cpu, struct tlb *tlb) {
DECLARE_LOCALS;

dword_t addr_offset = 0;
Expand Down Expand Up @@ -372,10 +372,10 @@ int CONCAT(decoder_name, OP_SIZE)(struct cpu_state *cpu) {
case 0x66:
#if OP_SIZE == 32
TRACELN("entering 16 bit mode");
return cpu_step16(cpu);
return cpu_step16(cpu, tlb);
#else
TRACELN("entering 32 bit mode");
return cpu_step32(cpu);
return cpu_step32(cpu, tlb);
#endif

case 0x67: TRACEI("address size prefix (ignored)"); goto restart;
Expand Down
24 changes: 16 additions & 8 deletions emu/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
union xmm_reg xmm_src; \
union xmm_reg xmm_dst

#define READMODRM modrm_decode32(cpu, &addr, &modrm)
#define READMODRM modrm_decode32(cpu, tlb, &addr, &modrm)
#define READIMM_(name,size) \
name = mem_read_(cpu->eip, size); \
cpu->eip += size/8; \
Expand Down Expand Up @@ -51,7 +51,7 @@

#define mem_read_type(addr, type) ({ \
type val; \
if (!mem_read(cpu->mem, addr, &val)) { \
if (!tlb_read(tlb, addr, &val)) { \
cpu->eip = saved_ip; \
cpu->segfault_addr = addr; \
return INT_GPF; \
Expand All @@ -60,7 +60,7 @@
})
#define mem_write_type(addr, val, type) ({ \
type _val = val; \
if (!mem_write(cpu->mem, addr, &_val)) { \
if (!tlb_write(tlb, addr, &_val)) { \
cpu->eip = saved_ip; \
cpu->segfault_addr = addr; \
return INT_GPF; \
Expand Down Expand Up @@ -619,15 +619,23 @@

flatten void cpu_run(struct cpu_state *cpu) {
int i = 0;
struct tlb *tlb = tlb_new(cpu->mem);
int changes = cpu->mem->changes;
while (true) {
int interrupt = cpu_step32(cpu);
int interrupt = cpu_step32(cpu, tlb);
if (interrupt == INT_NONE && i++ >= 100000) {
i = 0;
interrupt = INT_TIMER;
}
if (interrupt != INT_NONE) {
cpu->trapno = interrupt;
handle_interrupt(interrupt);
}
if (i++ >= 100000) {
i = 0;
receive_signals();
if (tlb->mem != cpu->mem)
tlb->mem = cpu->mem;
if (cpu->mem->changes != changes) {
tlb_flush(tlb);
changes = cpu->mem->changes;
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions emu/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
#define INT_FPU 7 // do not try to use the fpu. instead, try to realize the truth: there is no fpu.
#define INT_DOUBLE 8 // interrupt during interrupt, i.e. interruptception
#define INT_GPF 13
#define INT_TIMER 32
#define INT_SYSCALL 0x80
76 changes: 16 additions & 60 deletions emu/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@
#include "kernel/errno.h"
#include "emu/memory.h"

static void tlb_flush(struct mem *mem);

// this code currently assumes the system page size is 4k
// increment the change count
static void mem_changed(struct mem *mem);

struct mem *mem_new() {
struct mem *mem = malloc(sizeof(struct mem));
mem->refcount = 1;
mem->pt = calloc(MEM_PAGES, sizeof(struct pt_entry));
mem->tlb = malloc(TLB_SIZE * sizeof(struct tlb_entry));
tlb_flush(mem);
mem->changes = 0;
return mem;
}

Expand All @@ -31,7 +29,6 @@ void mem_release(struct mem *mem) {
if (mem->refcount-- == 0) {
pt_unmap(mem, 0, MEM_PAGES, PT_FORCE);
free(mem->pt);
free(mem->tlb);
free(mem);
}
}
Expand Down Expand Up @@ -71,7 +68,7 @@ int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned
mem->pt[page].offset = (page - start) << PAGE_BITS;
mem->pt[page].flags = flags;
}
tlb_flush(mem);
mem_changed(mem);
return 0;
}

Expand All @@ -92,7 +89,7 @@ int pt_unmap(struct mem *mem, page_t start, pages_t pages, int force) {
}
}
}
tlb_flush(mem);
mem_changed(mem);
return 0;
}

Expand All @@ -109,7 +106,7 @@ int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags) {
return _ENOMEM;
for (page_t page = start; page < start + pages; page++)
mem->pt[page].flags = flags;
tlb_flush(mem);
mem_changed(mem);
return 0;
}

Expand All @@ -127,44 +124,16 @@ int pt_copy_on_write(struct mem *src, page_t src_start, struct mem *dst, page_t
dst->pt[dst_page] = *entry;
}
}
tlb_flush(src);
tlb_flush(dst);
mem_changed(src);
mem_changed(dst);
return 0;
}

static void tlb_flush(struct mem *mem) {
memset(mem->tlb, 0, TLB_SIZE * sizeof(struct tlb_entry));
for (unsigned i = 0; i < TLB_SIZE; i++) {
mem->tlb[i].page = mem->tlb[i].page_if_writable = TLB_PAGE_EMPTY;
}
}

bool __mem_read_cross_page(struct mem *mem, addr_t addr, char *value, unsigned size) {
char *ptr1 = __mem_read_ptr(mem, addr);
char *ptr2 = __mem_read_ptr(mem, (PAGE(addr) + 1) << PAGE_BITS);
if (ptr1 == NULL || ptr2 == NULL)
return false;
size_t part1 = PAGE_SIZE - OFFSET(addr);
assert(part1 < size);
memcpy(value, ptr1, part1);
memcpy(value + part1, ptr2, size - part1);
return true;
static void mem_changed(struct mem *mem) {
mem->changes++;
}

bool __mem_write_cross_page(struct mem *mem, addr_t addr, const char *value, unsigned size) {
char *ptr1 = __mem_write_ptr(mem, addr);
char *ptr2 = __mem_write_ptr(mem, (PAGE(addr) + 1) << PAGE_BITS);
if (ptr1 == NULL || ptr2 == NULL)
return false;
size_t part1 = PAGE_SIZE - OFFSET(addr);
assert(part1 < size);
memcpy(ptr1, value, part1);
memcpy(ptr2, value + part1, size - part1);
return true;
}

int sys_getpid();
void *tlb_handle_miss(struct mem *mem, addr_t addr, int type) {
char *mem_ptr(struct mem *mem, addr_t addr, int type) {
page_t page = PAGE(addr);
struct pt_entry *entry = &mem->pt[page];

Expand All @@ -181,7 +150,7 @@ void *tlb_handle_miss(struct mem *mem, addr_t addr, int type) {
pt_map_nothing(mem, page, 1, P_WRITE | P_GROWSDOWN);
}

if (type == TLB_WRITE && !P_WRITABLE(entry->flags)) {
if (type == MEM_WRITE && !P_WRITABLE(entry->flags)) {
// page is unwritable or cow
// if page is cow, ~~milk~~ copy it
if (entry->flags & P_COW) {
Expand All @@ -195,23 +164,10 @@ void *tlb_handle_miss(struct mem *mem, addr_t addr, int type) {
}
}

// TODO if page is unwritable maybe we shouldn't bail and still add an
// entry to the TLB

struct tlb_entry *tlb = &mem->tlb[TLB_INDEX(addr)];
tlb->page = TLB_PAGE(addr);
if (P_WRITABLE(entry->flags))
tlb->page_if_writable = tlb->page;
else
// 1 is not a valid page so this won't look like a hit
tlb->page_if_writable = TLB_PAGE_EMPTY;
tlb->data_minus_addr = (uintptr_t) entry->data->data + entry->offset - TLB_PAGE(addr);
mem->dirty_page = TLB_PAGE(addr);
return (void *) (tlb->data_minus_addr + addr);
}

char *mem_ptr(struct mem *mem, addr_t addr) {
return __mem_read_ptr(mem, addr);
if (entry->data == NULL)
return NULL;
mem->dirty_page = addr & 0xfffff000;
return entry->data->data + entry->offset + OFFSET(addr);
}

size_t real_page_size;
Expand Down
61 changes: 4 additions & 57 deletions emu/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ typedef dword_t page_t;
struct mem {
unsigned refcount;
struct pt_entry *pt; // TODO replace with red-black tree
struct tlb_entry *tlb;
unsigned changes; // increment whenever a tlb flush is needed
page_t dirty_page;
};
#define MEM_PAGES (1 << 20) // at least on 32-bit
Expand Down Expand Up @@ -72,62 +72,9 @@ int pt_set_flags(struct mem *mem, page_t start, pages_t pages, int flags);
// Copy pages from src memory to dst memory using copy-on-write
int pt_copy_on_write(struct mem *src, page_t src_start, struct mem *dst, page_t dst_start, page_t pages);

struct tlb_entry {
page_t page;
page_t page_if_writable;
uintptr_t data_minus_addr;
};
#define TLB_BITS 10
#define TLB_SIZE (1 << TLB_BITS)
#define TLB_INDEX(addr) ((addr >> PAGE_BITS) & (TLB_SIZE - 1))
#define TLB_READ 0
#define TLB_WRITE 1
#define TLB_PAGE(addr) (addr & 0xfffff000)
#define TLB_PAGE_EMPTY 1
void *tlb_handle_miss(struct mem *mem, addr_t addr, int type);

forceinline void *__mem_read_ptr(struct mem *mem, addr_t addr) {
struct tlb_entry entry = mem->tlb[TLB_INDEX(addr)];
if (entry.page == TLB_PAGE(addr)) {
void *address = (void *) (entry.data_minus_addr + addr);
postulate(address != NULL);
return address;
}
return tlb_handle_miss(mem, addr, TLB_READ);
}
bool __mem_read_cross_page(struct mem *mem, addr_t addr, char *value, unsigned size);
forceinline bool __mem_read(struct mem *mem, addr_t addr, void *out, unsigned size) {
if (OFFSET(addr) > PAGE_SIZE - size)
return __mem_read_cross_page(mem, addr, out, size);
void *ptr = __mem_read_ptr(mem, addr);
if (ptr == NULL)
return false;
memcpy(out, ptr, size);
return true;
}
#define mem_read(mem, addr, value) __mem_read(mem, addr, (value), sizeof(*(value)))

forceinline void *__mem_write_ptr(struct mem *mem, addr_t addr) {
struct tlb_entry entry = mem->tlb[TLB_INDEX(addr)];
if (entry.page_if_writable == TLB_PAGE(addr)) {
mem->dirty_page = TLB_PAGE(addr);
void *address = (void *) (entry.data_minus_addr + addr);
postulate(address != NULL);
return address;
}
return tlb_handle_miss(mem, addr, TLB_WRITE);
}
bool __mem_write_cross_page(struct mem *mem, addr_t addr, const char *value, unsigned size);
forceinline bool __mem_write(struct mem *mem, addr_t addr, const void *value, unsigned size) {
if (OFFSET(addr) > PAGE_SIZE - size)
return __mem_write_cross_page(mem, addr, value, size);
void *ptr = __mem_write_ptr(mem, addr);
if (ptr == NULL)
return false;
memcpy(ptr, value, size);
return true;
}
#define mem_write(mem, addr, value) __mem_write(mem, addr, (value), sizeof(*(value)))
#define MEM_READ 0
#define MEM_WRITE 1
char *mem_ptr(struct mem *mem, addr_t addr, int type);

extern size_t real_page_size;

Expand Down
10 changes: 5 additions & 5 deletions emu/modrm.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ extern inline struct modrm_info modrm_get_info(byte_t byte);
// them, and returns everything in out parameters.
// TODO currently only does 32-bit
// FIXME doesn't check for segfaults
void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out) {
void modrm_decode32(struct cpu_state *cpu, struct tlb *tlb, addr_t *addr_out, struct modrm_info *info_out) {
byte_t modrm;
mem_read(cpu->mem, cpu->eip, &modrm);
tlb_read(tlb, cpu->eip, &modrm);
struct modrm_info info = modrm_get_info(modrm);
cpu->eip++;
*info_out = info;
Expand All @@ -98,7 +98,7 @@ void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *
} else {
// sib is simple enough to not use a table for
byte_t sib;
mem_read(cpu->mem, cpu->eip, &sib);
tlb_read(tlb, cpu->eip, &sib);
TRACE("sib %x ", sib);
cpu->eip++;
dword_t reg = 0;
Expand Down Expand Up @@ -139,15 +139,15 @@ void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *
switch (info.type) {
case mod_disp8: {
int8_t disp;
mem_read(cpu->mem, cpu->eip, &disp);
tlb_read(tlb, cpu->eip, &disp);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip++;
break;
}
case mod_disp32: {
int32_t disp;
mem_read(cpu->mem, cpu->eip, &disp);
tlb_read(tlb, cpu->eip, &disp);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip += 4;
Expand Down
2 changes: 1 addition & 1 deletion emu/modrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,6 @@ static inline struct modrm_info modrm_get_info(byte_t byte) {
#define REG(byte) ((byte & 0b00111000) >> 3)
#define RM(byte) ((byte & 0b00000111) >> 0)

void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out);
void modrm_decode32(struct cpu_state *cpu, struct tlb *tlb, addr_t *addr_out, struct modrm_info *info_out);

#endif
Loading

0 comments on commit 255f8f1

Please sign in to comment.