Moving all kernel files around just to fuck with whoever's keeping track ;)
This commit is contained in:
17
src/xenia/kernel/objects/sources.gypi
Normal file
17
src/xenia/kernel/objects/sources.gypi
Normal file
@@ -0,0 +1,17 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'xevent.cc',
|
||||
'xevent.h',
|
||||
'xfile.cc',
|
||||
'xfile.h',
|
||||
'xmodule.cc',
|
||||
'xmodule.h',
|
||||
'xnotify_listener.cc',
|
||||
'xnotify_listener.h',
|
||||
'xsemaphore.cc',
|
||||
'xsemaphore.h',
|
||||
'xthread.cc',
|
||||
'xthread.h',
|
||||
],
|
||||
}
|
||||
101
src/xenia/kernel/objects/xevent.cc
Normal file
101
src/xenia/kernel/objects/xevent.cc
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/objects/xevent.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
XEvent::XEvent(KernelState* kernel_state) :
|
||||
XObject(kernel_state, kTypeEvent),
|
||||
handle_(NULL) {
|
||||
}
|
||||
|
||||
XEvent::~XEvent() {
|
||||
if (handle_) {
|
||||
CloseHandle(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void XEvent::Initialize(bool manual_reset, bool initial_state) {
|
||||
XEASSERTNULL(handle_);
|
||||
|
||||
handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL);
|
||||
}
|
||||
|
||||
void XEvent::InitializeNative(void* native_ptr, DISPATCH_HEADER& header) {
|
||||
XEASSERTNULL(handle_);
|
||||
|
||||
bool manual_reset;
|
||||
switch (header.type_flags >> 24) {
|
||||
case 0x00: // EventNotificationObject (manual reset)
|
||||
manual_reset = true;
|
||||
break;
|
||||
case 0x01: // EventSynchronizationObject (auto reset)
|
||||
manual_reset = false;
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
return;
|
||||
}
|
||||
|
||||
bool initial_state = header.signal_state ? true : false;
|
||||
|
||||
handle_ = CreateEvent(NULL, manual_reset, initial_state, NULL);
|
||||
}
|
||||
|
||||
int32_t XEvent::Set(uint32_t priority_increment, bool wait) {
|
||||
return SetEvent(handle_) ? 1 : 0;
|
||||
}
|
||||
|
||||
int32_t XEvent::Reset() {
|
||||
return ResetEvent(handle_) ? 1 : 0;
|
||||
}
|
||||
|
||||
void XEvent::Clear() {
|
||||
ResetEvent(handle_);
|
||||
}
|
||||
|
||||
X_STATUS XEvent::Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
DWORD timeout_ms;
|
||||
if (opt_timeout) {
|
||||
int64_t timeout_ticks = (int64_t)(*opt_timeout);
|
||||
if (timeout_ticks > 0) {
|
||||
// Absolute time, based on January 1, 1601.
|
||||
// TODO(benvanik): convert time to relative time.
|
||||
XEASSERTALWAYS();
|
||||
timeout_ms = 0;
|
||||
} else if (timeout_ticks < 0) {
|
||||
// Relative time.
|
||||
timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS
|
||||
} else {
|
||||
timeout_ms = 0;
|
||||
}
|
||||
} else {
|
||||
timeout_ms = INFINITE;
|
||||
}
|
||||
|
||||
DWORD result = WaitForSingleObjectEx(handle_, timeout_ms, alertable);
|
||||
switch (result) {
|
||||
case WAIT_OBJECT_0:
|
||||
return X_STATUS_SUCCESS;
|
||||
case WAIT_IO_COMPLETION:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case WAIT_TIMEOUT:
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case WAIT_FAILED:
|
||||
case WAIT_ABANDONED:
|
||||
return X_STATUS_ABANDONED_WAIT_0;
|
||||
}
|
||||
}
|
||||
46
src/xenia/kernel/objects/xevent.h
Normal file
46
src/xenia/kernel/objects/xevent.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XBOXKRNL_XEVENT_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XEVENT_H_
|
||||
|
||||
#include <xenia/kernel/xobject.h>
|
||||
|
||||
#include <xenia/xbox.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
||||
class XEvent : public XObject {
|
||||
public:
|
||||
XEvent(KernelState* kernel_state);
|
||||
virtual ~XEvent();
|
||||
|
||||
void Initialize(bool manual_reset, bool initial_state);
|
||||
void InitializeNative(void* native_ptr, DISPATCH_HEADER& header);
|
||||
|
||||
int32_t Set(uint32_t priority_increment, bool wait);
|
||||
int32_t Reset();
|
||||
void Clear();
|
||||
|
||||
virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout);
|
||||
|
||||
private:
|
||||
HANDLE handle_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_XBOXKRNL_XEVENT_H_
|
||||
61
src/xenia/kernel/objects/xfile.cc
Normal file
61
src/xenia/kernel/objects/xfile.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/objects/xfile.h>
|
||||
|
||||
#include <xenia/kernel/async_request.h>
|
||||
#include <xenia/kernel/objects/xevent.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
XFile::XFile(KernelState* kernel_state, uint32_t desired_access) :
|
||||
desired_access_(desired_access), position_(0),
|
||||
XObject(kernel_state, kTypeFile) {
|
||||
async_event_ = new XEvent(kernel_state);
|
||||
async_event_->Initialize(false, false);
|
||||
}
|
||||
|
||||
XFile::~XFile() {
|
||||
// TODO(benvanik): signal that the file is closing?
|
||||
async_event_->Set(0, false);
|
||||
async_event_->Delete();
|
||||
}
|
||||
|
||||
X_STATUS XFile::Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
// Wait until some async operation completes.
|
||||
return async_event_->Wait(
|
||||
wait_reason, processor_mode, alertable, opt_timeout);
|
||||
}
|
||||
|
||||
X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_read) {
|
||||
if (byte_offset == -1) {
|
||||
// Read from current position.
|
||||
byte_offset = position_;
|
||||
}
|
||||
X_STATUS result = ReadSync(buffer, buffer_length, byte_offset, out_bytes_read);
|
||||
if (XSUCCEEDED(result)) {
|
||||
position_ += *out_bytes_read;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
X_STATUS XFile::Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
XAsyncRequest* request) {
|
||||
// Also tack on our event so that any waiters wake.
|
||||
request->AddWaitEvent(async_event_);
|
||||
position_ = byte_offset;
|
||||
//return entry_->ReadAsync(buffer, buffer_length, byte_offset, request);
|
||||
X_STATUS result = X_STATUS_NOT_IMPLEMENTED;
|
||||
return result;
|
||||
}
|
||||
85
src/xenia/kernel/objects/xfile.h
Normal file
85
src/xenia/kernel/objects/xfile.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XBOXKRNL_XFILE_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XFILE_H_
|
||||
|
||||
#include <xenia/kernel/xobject.h>
|
||||
|
||||
#include <xenia/xbox.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
class XAsyncRequest;
|
||||
class XEvent;
|
||||
|
||||
class XFileInfo {
|
||||
public:
|
||||
// FILE_NETWORK_OPEN_INFORMATION
|
||||
uint64_t creation_time;
|
||||
uint64_t last_access_time;
|
||||
uint64_t last_write_time;
|
||||
uint64_t change_time;
|
||||
uint64_t allocation_size;
|
||||
uint64_t file_length;
|
||||
X_FILE_ATTRIBUTES attributes;
|
||||
|
||||
void Write(uint8_t* base, uint32_t p) {
|
||||
XESETUINT64BE(base + p, creation_time);
|
||||
XESETUINT64BE(base + p + 8, last_access_time);
|
||||
XESETUINT64BE(base + p + 16, last_write_time);
|
||||
XESETUINT64BE(base + p + 24, change_time);
|
||||
XESETUINT64BE(base + p + 32, allocation_size);
|
||||
XESETUINT64BE(base + p + 40, file_length);
|
||||
XESETUINT32BE(base + p + 48, attributes);
|
||||
XESETUINT32BE(base + p + 52, 0); // pad
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class XFile : public XObject {
|
||||
public:
|
||||
virtual ~XFile();
|
||||
|
||||
size_t position() const { return position_; }
|
||||
void set_position(size_t value) { position_ = value; }
|
||||
|
||||
virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout);
|
||||
|
||||
virtual X_STATUS QueryInfo(XFileInfo* out_info) = 0;
|
||||
|
||||
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_read);
|
||||
X_STATUS Read(void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
XAsyncRequest* request);
|
||||
|
||||
protected:
|
||||
XFile(KernelState* kernel_state, uint32_t desired_access);
|
||||
virtual X_STATUS ReadSync(
|
||||
void* buffer, size_t buffer_length, size_t byte_offset,
|
||||
size_t* out_bytes_read) = 0;
|
||||
|
||||
private:
|
||||
uint32_t desired_access_;
|
||||
XEvent* async_event_;
|
||||
|
||||
// TODO(benvanik): create flags, open state, etc.
|
||||
|
||||
size_t position_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_XBOXKRNL_XFILE_H_
|
||||
341
src/xenia/kernel/objects/xmodule.cc
Normal file
341
src/xenia/kernel/objects/xmodule.cc
Normal file
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/objects/xmodule.h>
|
||||
|
||||
#include <xenia/emulator.h>
|
||||
#include <xenia/cpu/cpu.h>
|
||||
#include <xenia/kernel/objects/xthread.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
}
|
||||
|
||||
|
||||
XModule::XModule(KernelState* kernel_state, const char* path) :
|
||||
XObject(kernel_state, kTypeModule),
|
||||
xex_(NULL) {
|
||||
XEIGNORE(xestrcpya(path_, XECOUNT(path_), path));
|
||||
const char* slash = xestrrchra(path, '/');
|
||||
if (!slash) {
|
||||
slash = xestrrchra(path, '\\');
|
||||
}
|
||||
if (slash) {
|
||||
XEIGNORE(xestrcpya(name_, XECOUNT(name_), slash + 1));
|
||||
}
|
||||
char* dot = xestrrchra(name_, '.');
|
||||
if (dot) {
|
||||
*dot = 0;
|
||||
}
|
||||
}
|
||||
|
||||
XModule::~XModule() {
|
||||
xe_xex2_release(xex_);
|
||||
}
|
||||
|
||||
const char* XModule::path() {
|
||||
return path_;
|
||||
}
|
||||
|
||||
const char* XModule::name() {
|
||||
return name_;
|
||||
}
|
||||
|
||||
xe_xex2_ref XModule::xex() {
|
||||
return xe_xex2_retain(xex_);
|
||||
}
|
||||
|
||||
const xe_xex2_header_t* XModule::xex_header() {
|
||||
return xe_xex2_get_header(xex_);
|
||||
}
|
||||
|
||||
X_STATUS XModule::LoadFromFile(const char* path) {
|
||||
// Resolve the file to open.
|
||||
// TODO(benvanik): make this code shared?
|
||||
fs::Entry* fs_entry = kernel_state()->file_system()->ResolvePath(path);
|
||||
if (!fs_entry) {
|
||||
XELOGE("File not found: %s", path);
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
if (fs_entry->type() != fs::Entry::kTypeFile) {
|
||||
XELOGE("Invalid file type: %s", path);
|
||||
return X_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
// Map into memory.
|
||||
fs::MemoryMapping* mmap = fs_entry->CreateMemoryMapping(kXEFileModeRead, 0, 0);
|
||||
if (!mmap) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Load the module.
|
||||
X_STATUS return_code = LoadFromMemory(mmap->address(), mmap->length());
|
||||
|
||||
// Unmap memory and cleanup.
|
||||
delete mmap;
|
||||
delete fs_entry;
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
Processor* processor = kernel_state()->processor();
|
||||
XenonRuntime* runtime = processor->runtime();
|
||||
XexModule* xex_module = NULL;
|
||||
|
||||
// Load the XEX into memory and decrypt.
|
||||
xe_xex2_options_t xex_options;
|
||||
xe_zero_struct(&xex_options, sizeof(xex_options));
|
||||
xex_ = xe_xex2_load(kernel_state()->memory(), addr, length, xex_options);
|
||||
XEEXPECTNOTNULL(xex_);
|
||||
|
||||
// Prepare the module for execution.
|
||||
// Runtime takes ownership.
|
||||
xex_module = new XexModule(runtime);
|
||||
XEEXPECTZERO(xex_module->Load(name_, path_, xex_));
|
||||
XEEXPECTZERO(runtime->AddModule(xex_module));
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
|
||||
XECLEANUP:
|
||||
delete xex_module;
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
X_STATUS XModule::GetSection(const char* name,
|
||||
uint32_t* out_data, uint32_t* out_size) {
|
||||
const PESection* section = xe_xex2_get_pe_section(xex_, name);
|
||||
if (!section) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
*out_data = section->address;
|
||||
*out_size = section->size;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void* XModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||
// TODO(benvanik): check export tables.
|
||||
XELOGE("GetProcAddressByOrdinal not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
X_STATUS XModule::Launch(uint32_t flags) {
|
||||
const xe_xex2_header_t* header = xex_header();
|
||||
|
||||
XELOGI("Launching module...");
|
||||
|
||||
Dump();
|
||||
|
||||
// Create a thread to run in.
|
||||
XThread* thread = new XThread(
|
||||
kernel_state(),
|
||||
header->exe_stack_size,
|
||||
0,
|
||||
header->exe_entry_point, NULL,
|
||||
0);
|
||||
|
||||
X_STATUS result = thread->Create();
|
||||
if (XFAILED(result)) {
|
||||
XELOGE("Could not create launch thread: %.8X", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Wait until thread completes.
|
||||
thread->Wait(0, 0, 0, NULL);
|
||||
|
||||
thread->Release();
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void XModule::Dump() {
|
||||
ExportResolver* export_resolver =
|
||||
kernel_state_->emulator()->export_resolver();
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
|
||||
// XEX info.
|
||||
printf("Module %s:\n\n", path_);
|
||||
printf(" Module Flags: %.8X\n", header->module_flags);
|
||||
printf(" System Flags: %.8X\n", header->system_flags);
|
||||
printf("\n");
|
||||
printf(" Address: %.8X\n", header->exe_address);
|
||||
printf(" Entry Point: %.8X\n", header->exe_entry_point);
|
||||
printf(" Stack Size: %.8X\n", header->exe_stack_size);
|
||||
printf(" Heap Size: %.8X\n", header->exe_heap_size);
|
||||
printf("\n");
|
||||
printf(" Execution Info:\n");
|
||||
printf(" Media ID: %.8X\n", header->execution_info.media_id);
|
||||
printf(" Version: %d.%d.%d.%d\n",
|
||||
header->execution_info.version.major,
|
||||
header->execution_info.version.minor,
|
||||
header->execution_info.version.build,
|
||||
header->execution_info.version.qfe);
|
||||
printf(" Base Version: %d.%d.%d.%d\n",
|
||||
header->execution_info.base_version.major,
|
||||
header->execution_info.base_version.minor,
|
||||
header->execution_info.base_version.build,
|
||||
header->execution_info.base_version.qfe);
|
||||
printf(" Title ID: %.8X\n", header->execution_info.title_id);
|
||||
printf(" Platform: %.8X\n", header->execution_info.platform);
|
||||
printf(" Exec Table: %.8X\n", header->execution_info.executable_table);
|
||||
printf(" Disc Number: %d\n", header->execution_info.disc_number);
|
||||
printf(" Disc Count: %d\n", header->execution_info.disc_count);
|
||||
printf(" Savegame ID: %.8X\n", header->execution_info.savegame_id);
|
||||
printf("\n");
|
||||
printf(" Loader Info:\n");
|
||||
printf(" Image Flags: %.8X\n", header->loader_info.image_flags);
|
||||
printf(" Game Regions: %.8X\n", header->loader_info.game_regions);
|
||||
printf(" Media Flags: %.8X\n", header->loader_info.media_flags);
|
||||
printf("\n");
|
||||
printf(" TLS Info:\n");
|
||||
printf(" Slot Count: %d\n", header->tls_info.slot_count);
|
||||
printf(" Data Size: %db\n", header->tls_info.data_size);
|
||||
printf(" Address: %.8X, %db\n", header->tls_info.raw_data_address,
|
||||
header->tls_info.raw_data_size);
|
||||
printf("\n");
|
||||
printf(" Headers:\n");
|
||||
for (size_t n = 0; n < header->header_count; n++) {
|
||||
const xe_xex2_opt_header_t* opt_header = &header->headers[n];
|
||||
printf(" %.8X (%.8X, %4db) %.8X = %11d\n",
|
||||
opt_header->key, opt_header->offset, opt_header->length,
|
||||
opt_header->value, opt_header->value);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Resources.
|
||||
printf("Resources:\n");
|
||||
printf(" %.8X, %db\n", header->resource_info.address,
|
||||
header->resource_info.size);
|
||||
printf(" TODO\n");
|
||||
printf("\n");
|
||||
|
||||
// Section info.
|
||||
printf("Sections:\n");
|
||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
const char* type = "UNKNOWN";
|
||||
switch (section->info.type) {
|
||||
case XEX_SECTION_CODE:
|
||||
type = "CODE ";
|
||||
break;
|
||||
case XEX_SECTION_DATA:
|
||||
type = "RWDATA ";
|
||||
break;
|
||||
case XEX_SECTION_READONLY_DATA:
|
||||
type = "RODATA ";
|
||||
break;
|
||||
}
|
||||
const size_t start_address =
|
||||
header->exe_address + (i * xe_xex2_section_length);
|
||||
const size_t end_address =
|
||||
start_address + (section->info.page_count * xe_xex2_section_length);
|
||||
printf(" %3d %s %3d pages %.8X - %.8X (%d bytes)\n",
|
||||
(int)n, type, section->info.page_count, (int)start_address,
|
||||
(int)end_address, section->info.page_count * xe_xex2_section_length);
|
||||
i += section->info.page_count;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Static libraries.
|
||||
printf("Static Libraries:\n");
|
||||
for (size_t n = 0; n < header->static_library_count; n++) {
|
||||
const xe_xex2_static_library_t *library = &header->static_libraries[n];
|
||||
printf(" %-8s : %d.%d.%d.%d\n",
|
||||
library->name, library->major,
|
||||
library->minor, library->build, library->qfe);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Imports.
|
||||
printf("Imports:\n");
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t* library = &header->import_libraries[n];
|
||||
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
if (!xe_xex2_get_import_infos(xex_, library,
|
||||
&import_infos, &import_info_count)) {
|
||||
printf(" %s - %d imports\n", library->name, (int)import_info_count);
|
||||
printf(" Version: %d.%d.%d.%d\n",
|
||||
library->version.major, library->version.minor,
|
||||
library->version.build, library->version.qfe);
|
||||
printf(" Min Version: %d.%d.%d.%d\n",
|
||||
library->min_version.major, library->min_version.minor,
|
||||
library->min_version.build, library->min_version.qfe);
|
||||
printf("\n");
|
||||
|
||||
// Counts.
|
||||
int known_count = 0;
|
||||
int unknown_count = 0;
|
||||
int impl_count = 0;
|
||||
int unimpl_count = 0;
|
||||
for (size_t m = 0; m < import_info_count; m++) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[m];
|
||||
KernelExport* kernel_export =
|
||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||
if (kernel_export) {
|
||||
known_count++;
|
||||
if (kernel_export->is_implemented) {
|
||||
impl_count++;
|
||||
} else {
|
||||
unimpl_count++;
|
||||
}
|
||||
} else {
|
||||
unknown_count++;
|
||||
unimpl_count++;
|
||||
}
|
||||
}
|
||||
printf(" Total: %4u\n", import_info_count);
|
||||
printf(" Known: %3d%% (%d known, %d unknown)\n",
|
||||
(int)(known_count / (float)import_info_count * 100.0f),
|
||||
known_count, unknown_count);
|
||||
printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
|
||||
(int)(impl_count / (float)import_info_count * 100.0f),
|
||||
impl_count, unimpl_count);
|
||||
printf("\n");
|
||||
|
||||
// Listing.
|
||||
for (size_t m = 0; m < import_info_count; m++) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[m];
|
||||
KernelExport* kernel_export = export_resolver->GetExportByOrdinal(
|
||||
library->name, info->ordinal);
|
||||
const char *name = "UNKNOWN";
|
||||
bool implemented = false;
|
||||
if (kernel_export) {
|
||||
name = kernel_export->name;
|
||||
implemented = kernel_export->is_implemented;
|
||||
}
|
||||
if (kernel_export && kernel_export->type == KernelExport::Variable) {
|
||||
printf(" V %.8X %.3X (%3d) %s %s\n",
|
||||
info->value_address, info->ordinal, info->ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
} else if (info->thunk_address) {
|
||||
printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
info->value_address, info->thunk_address, info->ordinal,
|
||||
info->ordinal, implemented ? " " : "!!", name);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
xe_free(import_infos);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Exports.
|
||||
printf("Exports:\n");
|
||||
printf(" TODO\n");
|
||||
printf("\n");
|
||||
}
|
||||
60
src/xenia/kernel/objects/xmodule.h
Normal file
60
src/xenia/kernel/objects/xmodule.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XBOXKRNL_XMODULE_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XMODULE_H_
|
||||
|
||||
#include <xenia/kernel/xobject.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/export_resolver.h>
|
||||
#include <xenia/xbox.h>
|
||||
#include <xenia/kernel/util/xex2.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
||||
class XModule : public XObject {
|
||||
public:
|
||||
XModule(KernelState* kernel_state, const char* path);
|
||||
virtual ~XModule();
|
||||
|
||||
const char* path();
|
||||
const char* name();
|
||||
xe_xex2_ref xex();
|
||||
const xe_xex2_header_t* xex_header();
|
||||
|
||||
X_STATUS LoadFromFile(const char* path);
|
||||
X_STATUS LoadFromMemory(const void* addr, const size_t length);
|
||||
|
||||
X_STATUS GetSection(const char* name, uint32_t* out_data, uint32_t* out_size);
|
||||
void* GetProcAddressByOrdinal(uint16_t ordinal);
|
||||
|
||||
X_STATUS Launch(uint32_t flags);
|
||||
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
int LoadPE();
|
||||
|
||||
char name_[256];
|
||||
char path_[XE_MAX_PATH];
|
||||
|
||||
xe_xex2_ref xex_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_XBOXKRNL_XMODULE_H_
|
||||
125
src/xenia/kernel/objects/xnotify_listener.cc
Normal file
125
src/xenia/kernel/objects/xnotify_listener.cc
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/objects/xnotify_listener.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
XNotifyListener::XNotifyListener(KernelState* kernel_state) :
|
||||
XObject(kernel_state, kTypeNotifyListener),
|
||||
wait_handle_(NULL), lock_(0), mask_(0), notification_count_(0) {
|
||||
}
|
||||
|
||||
XNotifyListener::~XNotifyListener() {
|
||||
xe_mutex_free(lock_);
|
||||
if (wait_handle_) {
|
||||
CloseHandle(wait_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void XNotifyListener::Initialize(uint64_t mask) {
|
||||
XEASSERTNULL(wait_handle_);
|
||||
|
||||
lock_ = xe_mutex_alloc();
|
||||
wait_handle_ = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
mask_ = mask;
|
||||
}
|
||||
|
||||
void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) {
|
||||
xe_mutex_lock(lock_);
|
||||
auto existing = notifications_.find(id);
|
||||
if (existing != notifications_.end()) {
|
||||
// Already exists. Overwrite.
|
||||
notifications_[id] = data;
|
||||
} else {
|
||||
// New.
|
||||
notification_count_++;
|
||||
}
|
||||
SetEvent(wait_handle_);
|
||||
xe_mutex_unlock(lock_);
|
||||
}
|
||||
|
||||
bool XNotifyListener::DequeueNotification(
|
||||
XNotificationID* out_id, uint32_t* out_data) {
|
||||
bool dequeued = false;
|
||||
xe_mutex_lock(lock_);
|
||||
if (notification_count_) {
|
||||
dequeued = true;
|
||||
auto it = notifications_.begin();
|
||||
*out_id = it->first;
|
||||
*out_data = it->second;
|
||||
notifications_.erase(it);
|
||||
notification_count_--;
|
||||
if (!notification_count_) {
|
||||
ResetEvent(wait_handle_);
|
||||
}
|
||||
}
|
||||
xe_mutex_unlock(lock_);
|
||||
return dequeued;
|
||||
}
|
||||
|
||||
bool XNotifyListener::DequeueNotification(
|
||||
XNotificationID id, uint32_t* out_data) {
|
||||
bool dequeued = false;
|
||||
xe_mutex_lock(lock_);
|
||||
if (notification_count_) {
|
||||
dequeued = true;
|
||||
auto it = notifications_.find(id);
|
||||
if (it != notifications_.end()) {
|
||||
*out_data = it->second;
|
||||
notifications_.erase(it);
|
||||
notification_count_--;
|
||||
if (!notification_count_) {
|
||||
ResetEvent(wait_handle_);
|
||||
}
|
||||
}
|
||||
}
|
||||
xe_mutex_unlock(lock_);
|
||||
return dequeued;
|
||||
}
|
||||
|
||||
// TODO(benvanik): move this to common class
|
||||
X_STATUS XNotifyListener::Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
DWORD timeout_ms;
|
||||
if (opt_timeout) {
|
||||
int64_t timeout_ticks = (int64_t)(*opt_timeout);
|
||||
if (timeout_ticks > 0) {
|
||||
// Absolute time, based on January 1, 1601.
|
||||
// TODO(benvanik): convert time to relative time.
|
||||
XEASSERTALWAYS();
|
||||
timeout_ms = 0;
|
||||
} else if (timeout_ticks < 0) {
|
||||
// Relative time.
|
||||
timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS
|
||||
} else {
|
||||
timeout_ms = 0;
|
||||
}
|
||||
} else {
|
||||
timeout_ms = INFINITE;
|
||||
}
|
||||
|
||||
DWORD result = WaitForSingleObjectEx(wait_handle_, timeout_ms, alertable);
|
||||
switch (result) {
|
||||
case WAIT_OBJECT_0:
|
||||
return X_STATUS_SUCCESS;
|
||||
case WAIT_IO_COMPLETION:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case WAIT_TIMEOUT:
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case WAIT_FAILED:
|
||||
case WAIT_ABANDONED:
|
||||
return X_STATUS_ABANDONED_WAIT_0;
|
||||
}
|
||||
}
|
||||
55
src/xenia/kernel/objects/xnotify_listener.h
Normal file
55
src/xenia/kernel/objects/xnotify_listener.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
// This should probably be in XAM, but I don't want to build an extensible
|
||||
// object system. Meh.
|
||||
|
||||
#ifndef XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_
|
||||
|
||||
#include <xenia/kernel/xobject.h>
|
||||
|
||||
#include <xenia/xbox.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
// Values seem to be all over the place - GUIDs?
|
||||
typedef uint32_t XNotificationID;
|
||||
|
||||
|
||||
class XNotifyListener : public XObject {
|
||||
public:
|
||||
XNotifyListener(KernelState* kernel_state);
|
||||
virtual ~XNotifyListener();
|
||||
|
||||
void Initialize(uint64_t mask);
|
||||
|
||||
void EnqueueNotification(XNotificationID id, uint32_t data);
|
||||
bool DequeueNotification(XNotificationID* out_id, uint32_t* out_data);
|
||||
bool DequeueNotification(XNotificationID id, uint32_t* out_data);
|
||||
|
||||
virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout);
|
||||
|
||||
private:
|
||||
HANDLE wait_handle_;
|
||||
xe_mutex_t* lock_;
|
||||
std::unordered_map<XNotificationID, uint32_t> notifications_;
|
||||
size_t notification_count_;
|
||||
uint64_t mask_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_XBOXKRNL_XNOTIFY_LISTENER_H_
|
||||
82
src/xenia/kernel/objects/xsemaphore.cc
Normal file
82
src/xenia/kernel/objects/xsemaphore.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/objects/xsemaphore.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
XSemaphore::XSemaphore(KernelState* kernel_state) :
|
||||
XObject(kernel_state, kTypeSemaphore),
|
||||
handle_(NULL) {
|
||||
}
|
||||
|
||||
XSemaphore::~XSemaphore() {
|
||||
if (handle_) {
|
||||
CloseHandle(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void XSemaphore::Initialize(int32_t initial_count, int32_t maximum_count) {
|
||||
XEASSERTNULL(handle_);
|
||||
|
||||
handle_ = CreateSemaphore(NULL, initial_count, maximum_count, NULL);
|
||||
}
|
||||
|
||||
void XSemaphore::InitializeNative(void* native_ptr, DISPATCH_HEADER& header) {
|
||||
XEASSERTNULL(handle_);
|
||||
|
||||
// NOT IMPLEMENTED
|
||||
// We expect Initialize to be called shortly.
|
||||
}
|
||||
|
||||
int32_t XSemaphore::ReleaseSemaphore(int32_t release_count) {
|
||||
LONG previous_count = 0;
|
||||
::ReleaseSemaphore(handle_, release_count, &previous_count);
|
||||
return previous_count;
|
||||
}
|
||||
|
||||
X_STATUS XSemaphore::Wait(
|
||||
uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
DWORD timeout_ms;
|
||||
if (opt_timeout) {
|
||||
int64_t timeout_ticks = (int64_t)(*opt_timeout);
|
||||
if (timeout_ticks > 0) {
|
||||
// Absolute time, based on January 1, 1601.
|
||||
// TODO(benvanik): convert time to relative time.
|
||||
XEASSERTALWAYS();
|
||||
timeout_ms = 0;
|
||||
} else if (timeout_ticks < 0) {
|
||||
// Relative time.
|
||||
timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS
|
||||
} else {
|
||||
timeout_ms = 0;
|
||||
}
|
||||
} else {
|
||||
timeout_ms = INFINITE;
|
||||
}
|
||||
|
||||
DWORD result = WaitForSingleObjectEx(handle_, timeout_ms, alertable);
|
||||
switch (result) {
|
||||
case WAIT_OBJECT_0:
|
||||
return X_STATUS_SUCCESS;
|
||||
case WAIT_IO_COMPLETION:
|
||||
// Or X_STATUS_ALERTED?
|
||||
return X_STATUS_USER_APC;
|
||||
case WAIT_TIMEOUT:
|
||||
return X_STATUS_TIMEOUT;
|
||||
default:
|
||||
case WAIT_FAILED:
|
||||
case WAIT_ABANDONED:
|
||||
return X_STATUS_ABANDONED_WAIT_0;
|
||||
}
|
||||
}
|
||||
44
src/xenia/kernel/objects/xsemaphore.h
Normal file
44
src/xenia/kernel/objects/xsemaphore.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_
|
||||
|
||||
#include <xenia/kernel/xobject.h>
|
||||
|
||||
#include <xenia/xbox.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
||||
class XSemaphore : public XObject {
|
||||
public:
|
||||
XSemaphore(KernelState* kernel_state);
|
||||
virtual ~XSemaphore();
|
||||
|
||||
void Initialize(int32_t initial_count, int32_t maximum_count);
|
||||
void InitializeNative(void* native_ptr, DISPATCH_HEADER& header);
|
||||
|
||||
int32_t ReleaseSemaphore(int32_t release_count);
|
||||
|
||||
virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout);
|
||||
|
||||
private:
|
||||
HANDLE handle_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_XBOXKRNL_XSEMAPHORE_H_
|
||||
385
src/xenia/kernel/objects/xthread.cc
Normal file
385
src/xenia/kernel/objects/xthread.cc
Normal file
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/kernel/objects/xthread.h>
|
||||
|
||||
#include <xenia/cpu/cpu.h>
|
||||
#include <xenia/kernel/xboxkrnl_threading.h>
|
||||
#include <xenia/kernel/objects/xevent.h>
|
||||
#include <xenia/kernel/objects/xmodule.h>
|
||||
|
||||
|
||||
using namespace alloy;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
static uint32_t next_xthread_id = 0;
|
||||
static uint32_t current_thread_tls = xeKeTlsAlloc();
|
||||
static xe_mutex_t* critical_region_ = xe_mutex_alloc(10000);
|
||||
static XThread* shared_kernel_thread_ = 0;
|
||||
}
|
||||
|
||||
|
||||
XThread::XThread(KernelState* kernel_state,
|
||||
uint32_t stack_size,
|
||||
uint32_t xapi_thread_startup,
|
||||
uint32_t start_address, uint32_t start_context,
|
||||
uint32_t creation_flags) :
|
||||
XObject(kernel_state, kTypeThread),
|
||||
thread_id_(++next_xthread_id),
|
||||
thread_handle_(0),
|
||||
thread_state_address_(0),
|
||||
thread_state_(0),
|
||||
event_(NULL),
|
||||
irql_(0) {
|
||||
creation_params_.stack_size = stack_size;
|
||||
creation_params_.xapi_thread_startup = xapi_thread_startup;
|
||||
creation_params_.start_address = start_address;
|
||||
creation_params_.start_context = start_context;
|
||||
creation_params_.creation_flags = creation_flags;
|
||||
|
||||
// Adjust stack size - min of 16k.
|
||||
if (creation_params_.stack_size < 16 * 1024 * 1024) {
|
||||
creation_params_.stack_size = 16 * 1024 * 1024;
|
||||
}
|
||||
|
||||
event_ = new XEvent(kernel_state);
|
||||
event_->Initialize(true, false);
|
||||
}
|
||||
|
||||
XThread::~XThread() {
|
||||
event_->Release();
|
||||
|
||||
PlatformDestroy();
|
||||
|
||||
if (thread_state_) {
|
||||
delete thread_state_;
|
||||
}
|
||||
if (tls_address_) {
|
||||
kernel_state()->memory()->HeapFree(tls_address_, 0);
|
||||
}
|
||||
if (thread_state_address_) {
|
||||
kernel_state()->memory()->HeapFree(thread_state_address_, 0);
|
||||
}
|
||||
|
||||
if (thread_handle_) {
|
||||
// TODO(benvanik): platform kill
|
||||
XELOGE("Thread disposed without exiting");
|
||||
}
|
||||
}
|
||||
|
||||
XThread* XThread::GetCurrentThread() {
|
||||
XThread* thread = (XThread*)xeKeTlsGetValue(current_thread_tls);
|
||||
if (!thread) {
|
||||
// Assume this is some shared interrupt thread/etc.
|
||||
XThread::EnterCriticalRegion();
|
||||
thread = shared_kernel_thread_;
|
||||
if (!thread) {
|
||||
thread = new XThread(
|
||||
KernelState::shared(), 32 * 1024, 0, 0, 0, 0);
|
||||
shared_kernel_thread_ = thread;
|
||||
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
||||
}
|
||||
XThread::LeaveCriticalRegion();
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
uint32_t XThread::GetCurrentThreadHandle() {
|
||||
XThread* thread = XThread::GetCurrentThread();
|
||||
return thread->handle();
|
||||
}
|
||||
|
||||
uint32_t XThread::GetCurrentThreadId(const uint8_t* thread_state_block) {
|
||||
return XEGETUINT32BE(thread_state_block + 0x14C);
|
||||
}
|
||||
|
||||
uint32_t XThread::thread_state() {
|
||||
return thread_state_address_;
|
||||
}
|
||||
|
||||
uint32_t XThread::thread_id() {
|
||||
return thread_id_;
|
||||
}
|
||||
|
||||
uint32_t XThread::last_error() {
|
||||
uint8_t *p = memory()->Translate(thread_state_address_);
|
||||
return XEGETUINT32BE(p + 0x160);
|
||||
}
|
||||
|
||||
void XThread::set_last_error(uint32_t error_code) {
|
||||
uint8_t *p = memory()->Translate(thread_state_address_);
|
||||
XESETUINT32BE(p + 0x160, error_code);
|
||||
}
|
||||
|
||||
X_STATUS XThread::Create() {
|
||||
// Allocate thread state block from heap.
|
||||
// This is set as r13 for user code and some special inlined Win32 calls
|
||||
// (like GetLastError/etc) will poke it directly.
|
||||
// We try to use it as our primary store of data just to keep things all
|
||||
// consistent.
|
||||
// 0x000: pointer to tls data
|
||||
// 0x100: pointer to self?
|
||||
// 0x14C: thread id
|
||||
// 0x150: if >0 then error states don't get set
|
||||
// 0x160: last error
|
||||
// So, at offset 0x100 we have a 4b pointer to offset 200, then have the
|
||||
// structure.
|
||||
thread_state_address_ = (uint32_t)memory()->HeapAlloc(
|
||||
0, 2048, MEMORY_FLAG_ZERO);
|
||||
if (!thread_state_address_) {
|
||||
XELOGW("Unable to allocate thread state block");
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
// Set native info.
|
||||
SetNativePointer(thread_state_address_);
|
||||
|
||||
XModule* module = kernel_state()->GetExecutableModule();
|
||||
|
||||
// Allocate TLS block.
|
||||
const xe_xex2_header_t* header = module->xex_header();
|
||||
uint32_t tls_size = header->tls_info.slot_count * header->tls_info.data_size;
|
||||
tls_address_ = (uint32_t)memory()->HeapAlloc(
|
||||
0, tls_size, MEMORY_FLAG_ZERO);
|
||||
if (!tls_address_) {
|
||||
XELOGW("Unable to allocate thread local storage block");
|
||||
module->Release();
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
// Copy in default TLS info.
|
||||
// TODO(benvanik): is this correct?
|
||||
memory()->Copy(
|
||||
tls_address_, header->tls_info.raw_data_address, tls_size);
|
||||
|
||||
// Setup the thread state block (last error/etc).
|
||||
uint8_t *p = memory()->Translate(thread_state_address_);
|
||||
XESETUINT32BE(p + 0x000, tls_address_);
|
||||
XESETUINT32BE(p + 0x100, thread_state_address_);
|
||||
XESETUINT32BE(p + 0x14C, thread_id_);
|
||||
XESETUINT32BE(p + 0x150, 0); // ?
|
||||
XESETUINT32BE(p + 0x160, 0); // last error
|
||||
|
||||
// Allocate processor thread state.
|
||||
// This is thread safe.
|
||||
thread_state_ = new XenonThreadState(
|
||||
kernel_state()->processor()->runtime(),
|
||||
thread_id_, creation_params_.stack_size, thread_state_address_);
|
||||
|
||||
X_STATUS return_code = PlatformCreate();
|
||||
if (XFAILED(return_code)) {
|
||||
XELOGW("Unable to create platform thread (%.8X)", return_code);
|
||||
module->Release();
|
||||
return return_code;
|
||||
}
|
||||
|
||||
module->Release();
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS XThread::Exit(int exit_code) {
|
||||
// TODO(benvanik): set exit code in thread state block
|
||||
|
||||
// TODO(benvanik); dispatch events? waiters? etc?
|
||||
event_->Set(0, false);
|
||||
|
||||
// NOTE: unless PlatformExit fails, expect it to never return!
|
||||
X_STATUS return_code = PlatformExit(exit_code);
|
||||
if (XFAILED(return_code)) {
|
||||
return return_code;
|
||||
}
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#if XE_PLATFORM(WIN32)
|
||||
|
||||
static uint32_t __stdcall XThreadStartCallbackWin32(void* param) {
|
||||
XThread* thread = reinterpret_cast<XThread*>(param);
|
||||
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
||||
thread->Execute();
|
||||
xeKeTlsSetValue(current_thread_tls, NULL);
|
||||
thread->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformCreate() {
|
||||
thread_handle_ = CreateThread(
|
||||
NULL,
|
||||
creation_params_.stack_size,
|
||||
(LPTHREAD_START_ROUTINE)XThreadStartCallbackWin32,
|
||||
this,
|
||||
creation_params_.creation_flags,
|
||||
NULL);
|
||||
if (!thread_handle_) {
|
||||
uint32_t last_error = GetLastError();
|
||||
// TODO(benvanik): translate?
|
||||
XELOGE("CreateThread failed with %d", last_error);
|
||||
return last_error;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void XThread::PlatformDestroy() {
|
||||
CloseHandle(reinterpret_cast<HANDLE>(thread_handle_));
|
||||
thread_handle_ = NULL;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformExit(int exit_code) {
|
||||
// NOTE: does not return.
|
||||
ExitThread(exit_code);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void* XThreadStartCallbackPthreads(void* param) {
|
||||
XThread* thread = reinterpret_cast<XThread*>(param);
|
||||
xeKeTlsSetValue(current_thread_tls, (uint64_t)thread);
|
||||
thread->Execute();
|
||||
xeKeTlsSetValue(current_thread_tls, NULL);
|
||||
thread->Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformCreate() {
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, creation_params_.stack_size);
|
||||
|
||||
int result_code;
|
||||
if (creation_params_.creation_flags & X_CREATE_SUSPENDED) {
|
||||
#if XE_PLATFORM(OSX)
|
||||
result_code = pthread_create_suspended_np(
|
||||
reinterpret_cast<pthread_t*>(&thread_handle_),
|
||||
&attr,
|
||||
&XThreadStartCallbackPthreads,
|
||||
this);
|
||||
#else
|
||||
// TODO(benvanik): pthread_create_suspended_np on linux
|
||||
XEASSERTALWAYS();
|
||||
#endif // OSX
|
||||
} else {
|
||||
result_code = pthread_create(
|
||||
reinterpret_cast<pthread_t*>(&thread_handle_),
|
||||
&attr,
|
||||
&XThreadStartCallbackPthreads,
|
||||
this);
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
switch (result_code) {
|
||||
case 0:
|
||||
// Succeeded!
|
||||
return X_STATUS_SUCCESS;
|
||||
default:
|
||||
case EAGAIN:
|
||||
return X_STATUS_NO_MEMORY;
|
||||
case EINVAL:
|
||||
case EPERM:
|
||||
return X_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
void XThread::PlatformDestroy() {
|
||||
// No-op?
|
||||
}
|
||||
|
||||
X_STATUS XThread::PlatformExit(int exit_code) {
|
||||
// NOTE: does not return.
|
||||
pthread_exit((void*)exit_code);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
void XThread::Execute() {
|
||||
// If a XapiThreadStartup value is present, we use that as a trampoline.
|
||||
// Otherwise, we are a raw thread.
|
||||
if (creation_params_.xapi_thread_startup) {
|
||||
kernel_state()->processor()->Execute(
|
||||
thread_state_,
|
||||
creation_params_.xapi_thread_startup,
|
||||
creation_params_.start_address, creation_params_.start_context);
|
||||
} else {
|
||||
// Run user code.
|
||||
int exit_code = (int)kernel_state()->processor()->Execute(
|
||||
thread_state_,
|
||||
creation_params_.start_address, creation_params_.start_context);
|
||||
// If we got here it means the execute completed without an exit being called.
|
||||
// Treat the return code as an implicit exit code.
|
||||
Exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS XThread::Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout) {
|
||||
return event_->Wait(wait_reason, processor_mode, alertable, opt_timeout);
|
||||
}
|
||||
|
||||
void XThread::EnterCriticalRegion() {
|
||||
// Global critical region. This isn't right, but is easy.
|
||||
xe_mutex_lock(critical_region_);
|
||||
}
|
||||
|
||||
void XThread::LeaveCriticalRegion() {
|
||||
xe_mutex_unlock(critical_region_);
|
||||
}
|
||||
|
||||
uint32_t XThread::RaiseIrql(uint32_t new_irql) {
|
||||
return xe_atomic_exchange_32(new_irql, &irql_);
|
||||
}
|
||||
|
||||
void XThread::LowerIrql(uint32_t new_irql) {
|
||||
irql_ = new_irql;
|
||||
}
|
||||
|
||||
X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
|
||||
DWORD result = ResumeThread(thread_handle_);
|
||||
if (result >= 0) {
|
||||
if (out_suspend_count) {
|
||||
*out_suspend_count = result;
|
||||
}
|
||||
return X_STATUS_SUCCESS;
|
||||
} else {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS XThread::Delay(
|
||||
uint32_t processor_mode, uint32_t alertable, uint64_t interval) {
|
||||
int64_t timeout_ticks = interval;
|
||||
DWORD timeout_ms;
|
||||
if (timeout_ticks > 0) {
|
||||
// Absolute time, based on January 1, 1601.
|
||||
// TODO(benvanik): convert time to relative time.
|
||||
XEASSERTALWAYS();
|
||||
timeout_ms = 0;
|
||||
} else if (timeout_ticks < 0) {
|
||||
// Relative time.
|
||||
timeout_ms = (DWORD)(-timeout_ticks / 10000); // Ticks -> MS
|
||||
} else {
|
||||
timeout_ms = 0;
|
||||
}
|
||||
DWORD result = SleepEx(timeout_ms, alertable ? TRUE : FALSE);
|
||||
switch (result) {
|
||||
case 0:
|
||||
return X_STATUS_SUCCESS;
|
||||
case WAIT_IO_COMPLETION:
|
||||
return X_STATUS_USER_APC;
|
||||
default:
|
||||
return X_STATUS_ALERTED;
|
||||
}
|
||||
}
|
||||
96
src/xenia/kernel/objects/xthread.h
Normal file
96
src/xenia/kernel/objects/xthread.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_XBOXKRNL_XTHREAD_H_
|
||||
#define XENIA_KERNEL_XBOXKRNL_XTHREAD_H_
|
||||
|
||||
#include <xenia/kernel/xobject.h>
|
||||
|
||||
#include <xenia/xbox.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
class XenonThreadState;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
||||
class XEvent;
|
||||
|
||||
|
||||
class XThread : public XObject {
|
||||
public:
|
||||
XThread(KernelState* kernel_state,
|
||||
uint32_t stack_size,
|
||||
uint32_t xapi_thread_startup,
|
||||
uint32_t start_address, uint32_t start_context,
|
||||
uint32_t creation_flags);
|
||||
virtual ~XThread();
|
||||
|
||||
static XThread* GetCurrentThread();
|
||||
static uint32_t GetCurrentThreadHandle();
|
||||
static uint32_t GetCurrentThreadId(const uint8_t* thread_state_block);
|
||||
|
||||
uint32_t thread_state();
|
||||
uint32_t thread_id();
|
||||
uint32_t last_error();
|
||||
void set_last_error(uint32_t error_code);
|
||||
|
||||
X_STATUS Create();
|
||||
X_STATUS Exit(int exit_code);
|
||||
|
||||
void Execute();
|
||||
|
||||
virtual X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode,
|
||||
uint32_t alertable, uint64_t* opt_timeout);
|
||||
|
||||
static void EnterCriticalRegion();
|
||||
static void LeaveCriticalRegion();
|
||||
uint32_t RaiseIrql(uint32_t new_irql);
|
||||
void LowerIrql(uint32_t new_irql);
|
||||
|
||||
X_STATUS Resume(uint32_t* out_suspend_count);
|
||||
X_STATUS Delay(
|
||||
uint32_t processor_mode, uint32_t alertable, uint64_t interval);
|
||||
|
||||
private:
|
||||
X_STATUS PlatformCreate();
|
||||
void PlatformDestroy();
|
||||
X_STATUS PlatformExit(int exit_code);
|
||||
|
||||
struct {
|
||||
uint32_t stack_size;
|
||||
uint32_t xapi_thread_startup;
|
||||
uint32_t start_address;
|
||||
uint32_t start_context;
|
||||
uint32_t creation_flags;
|
||||
} creation_params_;
|
||||
|
||||
uint32_t thread_id_;
|
||||
void* thread_handle_;
|
||||
uint32_t tls_address_;
|
||||
uint32_t thread_state_address_;
|
||||
cpu::XenonThreadState* thread_state_;
|
||||
|
||||
uint32_t irql_;
|
||||
|
||||
XEvent* event_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_KERNEL_XBOXKRNL_XTHREAD_H_
|
||||
Reference in New Issue
Block a user