Skip to content

Commit

Permalink
Implement TLB
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed Jun 1, 2017
1 parent 50b9fbd commit 3e4172d
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 93 deletions.
5 changes: 3 additions & 2 deletions emu/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ int cpu_step(struct cpu_state *cpu) {
#endif

// watch out: these macros can evaluate the arguments any number of times
#define MEM_(addr,size) MEM_READ(&cpu->mem, addr, size)
#define MEM_W_(addr,size) MEM_WRITE(&cpu->mem, addr, size)
#define MEM_(addr,size) (*(UINT(size) *) mem_read_ptr(&cpu->mem, addr))
#define MEM_W_(addr,size) (*(UINT(size) *) mem_write_ptr(&cpu->mem, addr))
#define MEM(addr) MEM_(addr,OP_SIZE)
#define MEM_W(addr) MEM_W_(addr,OP_SIZE)
#define MEM8(addr) MEM_(addr,8)
Expand Down Expand Up @@ -311,6 +311,7 @@ int cpu_step(struct cpu_state *cpu) {
READIMM8; J_REL(!LE, (int8_t) imm8); break;

case 0x80: TRACEI("grp1 imm8, modrm8");
// FIXME this casts uint8 to int32 which is wrong
READMODRM; READIMM8; GRP1(imm8, modrm_val8_w); break;
case 0x81: TRACEI("grp1 imm, modrm");
READMODRM; READIMM; GRP1(imm, modrm_val_w); break;
Expand Down
25 changes: 25 additions & 0 deletions emu/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

void mem_init(struct mem *mem) {
mem->pt = calloc(PT_SIZE, sizeof(struct pt_entry *));
mem->tlb = calloc(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;
}
}

int pt_map(struct mem *mem, page_t start, pages_t pages, void *memory, unsigned flags) {
Expand Down Expand Up @@ -69,6 +73,27 @@ void pt_dump(struct mem *mem) {
}
}

void *tlb_handle_miss(struct mem *mem, addr_t addr, int type) {
struct pt_entry *pt = mem->pt[PAGE(addr)];
if (pt == NULL)
return NULL; // page does not exist
if (type == TLB_WRITE && !(pt->flags & P_WRITABLE))
return NULL; // unwritable

// 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 (pt->flags & P_WRITABLE)
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 = pt->data;
return (char *) tlb->data + OFFSET(addr);
}

__attribute__((constructor))
static void check_pagesize(void) {
if (sysconf(_SC_PAGESIZE) != 1 << 12) {
Expand Down
41 changes: 30 additions & 11 deletions emu/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,12 @@

struct mem {
struct pt_entry **pt;
/* struct tlb_entry *tlb; */
struct tlb_entry *tlb;
};

// Initialize a mem struct
void mem_init(struct mem *mem);

#define MEM_READ(mem, addr, size) \
(*((const UINT(size) *) &((char *) (mem)->pt[PAGE(addr)]->data)[OFFSET(addr)]))
#define MEM_WRITE(mem, addr, size) \
(*((UINT(size) *) &((char *) (mem)->pt[PAGE(addr)]->data)[OFFSET(addr)]))

#define PAGE_BITS 12
#define PAGE_SIZE (1 << PAGE_BITS)
#define PAGE(addr) ((addr) >> PAGE_BITS)
Expand Down Expand Up @@ -45,10 +40,34 @@ void pt_unmap(struct mem *mem, page_t page, pages_t pages);

void pt_dump(struct mem *mem);

/* struct tlb_entry { */
/* page_t page; */
/* page_t page_if_writable; */
/* void *data; */
/* }; */
struct tlb_entry {
page_t page;
page_t page_if_writable;
void *data;
};
#define TLB_BITS 10
#define TLB_SIZE (1 << TLB_BITS)
#define TLB_INDEX(addr) (((addr) & 0x003ff000) >> 12)
#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)) {
return (char *) entry.data + OFFSET(addr);
}
return tlb_handle_miss(mem, addr, TLB_READ);
}

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)) {
return (char *) entry.data + OFFSET(addr);
}
return tlb_handle_miss(mem, addr, TLB_WRITE);
}

#endif
76 changes: 75 additions & 1 deletion emu/modrm.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,78 @@ extern inline struct modrm_info modrm_get_info(byte_t byte);

#endif

extern inline void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out);
// Decodes ModR/M and SIB byte pointed to by cpu->eip, increments cpu->eip past
// them, and returns everything in out parameters.
// TODO currently only does 32-bit
void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out) {
byte_t modrm = *(byte_t *) mem_read_ptr(&cpu->mem, cpu->eip);
struct modrm_info info = modrm_get_info(modrm);
cpu->eip++;
*info_out = info;
if (info.type == mod_reg) return;

if (!info.sib) {
if (info.modrm_regid.reg32_id != 0) {
*addr_out += REG_VAL(cpu, info.modrm_regid.reg32_id, 32);
}
} else {
// sib is simple enough to not use a table for
byte_t sib = *(byte_t *) mem_read_ptr(&cpu->mem, cpu->eip);
TRACE("sib %x ", sib);
cpu->eip++;
dword_t reg = 0;
switch (REG(sib)) {
case 0b000: reg += cpu->eax; break;
case 0b001: reg += cpu->ecx; break;
case 0b010: reg += cpu->edx; break;
case 0b011: reg += cpu->ebx; break;
case 0b101: reg += cpu->ebp; break;
case 0b110: reg += cpu->esi; break;
case 0b111: reg += cpu->edi; break;
}
switch (MOD(sib)) {
case 0b01: reg *= 2; break;
case 0b10: reg *= 4; break;
case 0b11: reg *= 8; break;
}
switch (RM(sib)) {
case 0b000: reg += cpu->eax; break;
case 0b001: reg += cpu->ecx; break;
case 0b010: reg += cpu->edx; break;
case 0b011: reg += cpu->ebx; break;
case 0b100: reg += cpu->esp; break;
case 0b101:
// i know this is weird but this is what intel says
if (info.type == mod_disp0) {
info.type = mod_disp32;
} else {
reg += cpu->ebp;
}
break;
case 0b110: reg += cpu->esi; break;
case 0b111: reg += cpu->edi; break;
}
*addr_out += reg;
}

int disp;
switch (info.type) {
case mod_disp8: {
disp = *(int8_t *) mem_read_ptr(&cpu->mem, cpu->eip);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip++;
break;
}
case mod_disp32: {
disp = *(int32_t *) mem_read_ptr(&cpu->mem, cpu->eip);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip += 4;
break;
}

// shut up compiler I don't want to handle other cases
default:;
}
}
76 changes: 1 addition & 75 deletions emu/modrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,78 +71,4 @@ static inline struct modrm_info modrm_get_info(byte_t byte) {
#define REG(byte) ((byte & 0b00111000) >> 3)
#define RM(byte) ((byte & 0b00000111) >> 0)

// Decodes ModR/M and SIB byte pointed to by cpu->eip, increments cpu->eip past
// them, and returns everything in out parameters.
// TODO currently only does 32-bit
static inline void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out) {
byte_t modrm = MEM_READ(&cpu->mem, cpu->eip, 8);
struct modrm_info info = modrm_get_info(modrm);
cpu->eip++;
*info_out = info;
if (info.type == mod_reg) return;

if (!info.sib) {
if (info.modrm_regid.reg32_id != 0) {
*addr_out += REG_VAL(cpu, info.modrm_regid.reg32_id, 32);
}
} else {
// sib is simple enough to not use a table for
byte_t sib = MEM_READ(&cpu->mem, cpu->eip, 8);
TRACE("sib %x ", sib);
cpu->eip++;
dword_t reg = 0;
switch (REG(sib)) {
case 0b000: reg += cpu->eax; break;
case 0b001: reg += cpu->ecx; break;
case 0b010: reg += cpu->edx; break;
case 0b011: reg += cpu->ebx; break;
case 0b101: reg += cpu->ebp; break;
case 0b110: reg += cpu->esi; break;
case 0b111: reg += cpu->edi; break;
}
switch (MOD(sib)) {
case 0b01: reg *= 2; break;
case 0b10: reg *= 4; break;
case 0b11: reg *= 8; break;
}
switch (RM(sib)) {
case 0b000: reg += cpu->eax; break;
case 0b001: reg += cpu->ecx; break;
case 0b010: reg += cpu->edx; break;
case 0b011: reg += cpu->ebx; break;
case 0b100: reg += cpu->esp; break;
case 0b101:
// i know this is weird but this is what intel says
if (info.type == mod_disp0) {
info.type = mod_disp32;
} else {
reg += cpu->ebp;
}
break;
case 0b110: reg += cpu->esi; break;
case 0b111: reg += cpu->edi; break;
}
*addr_out += reg;
}

int disp;
switch (info.type) {
case mod_disp8: {
disp = (int8_t) MEM_READ(&cpu->mem, cpu->eip, 8);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip++;
break;
}
case mod_disp32: {
disp = (int32_t) MEM_READ(&cpu->mem, cpu->eip, 32);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip += 4;
break;
}

// shut up compiler I don't want to handle other cases
default:;
}
}
void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out);
2 changes: 2 additions & 0 deletions misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
#define STR(x) _STR(x)
#define _STR(x) #x

// keywords
#define bits unsigned int
#define forceinline inline __attribute__((always_inline))

// types
// word_t will be 64-bit to read 64-bit elves
Expand Down
8 changes: 4 additions & 4 deletions sys/user.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
#include "emu/process.h"

dword_t user_get(addr_t addr) {
return MEM_READ(curmem, addr, 32);
return *(dword_t *) mem_read_ptr(curmem, addr);
}

byte_t user_get8(addr_t addr) {
return MEM_READ(curmem, addr, 8);
return *(byte_t *) mem_read_ptr(curmem, addr);
}

void user_put(addr_t addr, dword_t value) {
MEM_WRITE(curmem, addr, 32) = value;
*(dword_t *) mem_write_ptr(curmem, addr) = value;
}

void user_put8(addr_t addr, byte_t value) {
MEM_WRITE(curmem, addr, 8) = value;
*(byte_t *) mem_write_ptr(curmem, addr) = value;
}

int user_get_string(addr_t addr, char *buf, size_t max) {
Expand Down

0 comments on commit 3e4172d

Please sign in to comment.