Moving all kernel files around just to fuck with whoever's keeping track ;)

This commit is contained in:
Ben Vanik
2014-01-04 17:12:46 -08:00
parent aad4d7bebf
commit 4d92720109
108 changed files with 449 additions and 677 deletions

View 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',
],
}

View 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;
}
}

View 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_

View 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;
}

View 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_

View 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");
}

View 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_

View 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;
}
}

View 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_

View 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;
}
}

View 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_

View 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;
}
}

View 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_