-
Notifications
You must be signed in to change notification settings - Fork 85
/
libcpu-stubs.c
150 lines (133 loc) · 4.29 KB
/
libcpu-stubs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Stubs for libcpu to use
#include <stdint.h>
#include <stdlib.h>
#define UNUSED(x) (void)(x)
// You may want to do other things before aborting.
// In Halfix, it releases the mouse from SDL before aborting, which makes debugging in GDB infinitely easier.
void util_abort(void)
{
abort();
}
// Convert a pointer p to a physical address that the code cache can work with
uint32_t cpulib_ptr_to_phys(void* p)
{
UNUSED(p);
abort();
}
// Set to 1 if the APIC is enabled.
int apic_is_enabled(void)
{
return 1;
}
// Returns the interrupt sitting on the PIC.
uint8_t pic_get_interrupt(void)
{
return -1;
}
// You probably want to build your own state serializer.
void state_register(void* s)
{
UNUSED(s);
}
// Since you're only using the CPU, you can call cpu_reset directly
void io_register_reset(void* cb)
{
UNUSED(cb);
}
// Convert a physical RAM address to a pointer. The result is strongly recommended but not required to be 4,096 byte aligned.
// The minimum alignment is 16 bytes for various SSE operations.
void* get_phys_ram_ptr(uint32_t addr, int write)
{
UNUSED(addr | write);
abort();
}
// Given a linear address, convert to a physical address (useful for process emulators and mmap).
// This lets you emulate paging without actually setting up CR3 and page tables.
// The pipeline goes like this:
/*
void* ptr = get_lin_ram_ptr();
if (ptr != NULL)
// Address is valid and will be cached in the TLB
else {
if (fault)
// Trigger page fault
else
// Do physical address translation, with CR3 yadda yadda
}
*/
// Essentially, if this routine returns non-null, then it's assumed that paging succeeded
// If the routine returns null, then *fault is checked.
// You MUST set *fault to a value if returning NULL. It isn't initialized.
void* get_lin_ram_ptr(uint32_t addr, int flags, int* fault)
{
*fault = 0;
UNUSED(addr | flags);
UNUSED(fault);
abort();
}
// Handle a MMIO read. Possible sizes are 0 (byte), 1 (word), and 2 (dword).
// This function is called every time the following conditions are met:
/*
- The access is out of bounds (i.e. you write to to address 0x400001 when you only have 0x400000 bytes of memory).
This handily captures all higher-half MMIO accesses
- The access is a READ between 0xA0000 and 0xBFFFF, inclusive
- The access is a WRITE between 0xA0000 and 0xFFFFF, inclusive
*/
// addr will contain a physical address. Data may or may not be truncated to the proper size. Possible values of sizes will be 0, 1 and 2
void io_handle_mmio_write(uint32_t addr, uint32_t data, int size)
{
UNUSED(addr | data | size);
}
// Remember to truncate this value before returning it. So if the emulator requests size=1 (a word), don't give it 0xFFFF1234, for instance.
uint32_t io_handle_mmio_read(uint32_t addr, int size)
{
UNUSED(addr | size);
return 0;
}
// Returns an 8-bit value from a port. Serves as the backend of the IN instruction.
// This function is only triggered when the I/O port is actually read. If it doesn't succeed (i.e. Virtual 8086 Mode, IOPL, etc.), then this function isn't called.
// If you want something called every time the IN instruction is called (regardless of success), either use instrumentation or play around with IOPL.
// For INSB/INSW/INSD, the appropriate io_readb function will be called over and over again.
uint8_t io_readb(uint32_t port)
{
return port & 0;
}
uint16_t io_readw(uint32_t port)
{
return port & 0;
}
uint32_t io_readd(uint32_t port)
{
return port & 0;
}
// Writes a value to a port, the backend of the OUT instruction. This value is likely truncated properly, but don't take my word for it.
// Again, it's only called on success.
void io_writeb(uint32_t port, uint8_t data)
{
UNUSED(port | data);
}
void io_writew(uint32_t port, uint16_t data)
{
UNUSED(port | data);
}
void io_writed(uint32_t port, uint32_t data)
{
UNUSED(port | data);
}
// Raises an IRQ line. Only used by the FPU in case of a legacy exception.
// If the FPU handles an exception the normal way, this routine WILL NOT be called
void pic_raise_irq(int line)
{
UNUSED(line);
}
void pic_lower_irq(int line)
{
UNUSED(line);
}
#ifdef NO_LIBM
// Used purely for debugging purposes only. It's useful when poking through FPU internals but not in production code
double pow(double a, double b)
{
return a + b;
}
#endif