Merge branch 'master' into emunand_dev

This commit is contained in:
hexkyz 2019-04-13 18:23:23 +01:00
commit a1512cf30f
31 changed files with 351 additions and 20 deletions

View file

@ -53,6 +53,7 @@ dist: all
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
mkdir -p atmosphere-$(AMSVER)/atmosphere/fatal_errors
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin

View file

@ -19,7 +19,7 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 0
#define ATMOSPHERE_RELEASE_VERSION_MINOR 8
#define ATMOSPHERE_RELEASE_VERSION_MICRO 6
#define ATMOSPHERE_RELEASE_VERSION_MICRO 7
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 7
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0

View file

@ -1,4 +1,19 @@
# Changelog
## 0.8.7
+ A few bugs were fixed that could cause fatal to fail to show an error under certain circumstances.
+ A bug was fixed that caused an error when launching certain games (e.g. Hellblade: Senua's Sacrifice).
+ Loader had support added in ams-0.8.4 for a new (7.0.0+) flag bit in NPDMs during process creation, but forgot to allow this bit to be set when validating the NPDM.
+ dmnt's cheat virtual machine received new instructions.
+ These allow for saving, restoring, or clearing registers to a secondary bank, effectively doubling the number of values that can be stored.
+ SHA256 code has been swapped from linux code to libnx's new hw-accelerated cryptography API.
+ Extensions were added to smcGetInfo:
+ A ConfigItem was added to detect whether the current unit has the RCM bug patched.
+ A ConfigItem was added to retrieve the current Atmosphère build hash.
+ Exosphère now tells the kernel to enable user-mode exception handlers, which should allow for better crash reporting/detection from Atmosphère's modules in the future..
+ Opt-in support was added for redirecting game save files to directories on the SD card.
+ Please note, this feature is **experimental**, and may cause problems. Please use at your own risk (and back up your saves before enabling it), as it still needs testing.
+ This can be enabled by setting `atmosphere!fsmitm_redirect_saves_to_sd` to 1 in `system_settings.ini`.
+ General system stability improvements to enhance the user's experience.
## 0.8.6
+ A number of bugs were fixed, including:
+ A case of inverted logic was fixed in fs.mitm which prevented the flags system from working correctly.

View file

@ -123,7 +123,7 @@ uint64_t bootconfig_get_memory_arrangement(void) {
}
}
uint64_t bootconfig_get_kernel_memory_configuration(void) {
uint64_t bootconfig_get_kernel_configuration(void) {
if (bootconfig_is_debug_mode()) {
uint64_t high_val = 0;
if (fuse_get_dram_id() == 4) {

View file

@ -21,6 +21,14 @@
#include <stdint.h>
#include "memory_map.h"
/* This is kind of ConfigItem, but it's stored in BootConfig, so... */
typedef enum {
KERNELCONFIGFLAG_INITIALIZE_MEMORY_TO_PATTERN = (1 << 0),
KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS = (1 << 1),
KERNELCONFIGFLAG_ENABLE_USER_PMU_ACCESS = (1 << 2),
KERNELCONFIGFLAG_CALL_SMC_PANIC_ON_KERNEL_ERROR = (1 << 8),
} KernelConfigFlag;
/* This provides management for Switch BootConfig. */
#define LOADED_BOOTCONFIG (get_loaded_bootconfig())
@ -78,7 +86,7 @@ bool bootconfig_take_extabt_serror_to_el3(void);
uint64_t bootconfig_get_value_for_sysctr0(void);
uint64_t bootconfig_get_memory_arrangement(void);
uint64_t bootconfig_get_kernel_memory_configuration(void);
uint64_t bootconfig_get_kernel_configuration(void);
bool bootconfig_is_recovery_boot(void);
uint64_t bootconfig_get_boot_reason(void);

View file

@ -206,8 +206,13 @@ uint32_t configitem_get(bool privileged, ConfigItem item, uint64_t *p_outvalue)
*p_outvalue = (int)(bootconfig_is_debug_mode());
}
break;
case CONFIGITEM_KERNELMEMORYCONFIGURATION:
*p_outvalue = bootconfig_get_kernel_memory_configuration();
case CONFIGITEM_KERNELCONFIGURATION:
{
uint64_t config = bootconfig_get_kernel_configuration();
/* Always enable usermode exception handlers. */
config |= KERNELCONFIGFLAG_ENABLE_USER_EXCEPTION_HANDLERS;
*p_outvalue = config;
}
break;
case CONFIGITEM_BATTERYPROFILE:
*p_outvalue = (int)g_battery_profile;

View file

@ -32,7 +32,7 @@ typedef enum {
CONFIGITEM_BOOTREASON = 9,
CONFIGITEM_MEMORYARRANGE = 10,
CONFIGITEM_ISDEBUGMODE = 11,
CONFIGITEM_KERNELMEMORYCONFIGURATION = 12,
CONFIGITEM_KERNELCONFIGURATION = 12,
CONFIGITEM_BATTERYPROFILE = 13,
CONFIGITEM_ISQUESTUNIT = 14,
CONFIGITEM_NEWHARDWARETYPE_5X = 15,

View file

@ -90,9 +90,6 @@ static void setup_env(void) {
/* Initialize hardware. */
nx_hwinit();
/* Check for panics. */
check_and_display_panic();
/* Zero-fill the framebuffer and register it as printk provider. */
video_init(g_framebuffer);
@ -138,6 +135,9 @@ int main(void) {
/* Initialize the display, console, etc. */
setup_env();
/* Check for panics. */
check_and_display_panic();
/* Load the BCT0 configuration ini off of the SD. */
bct0 = load_config();

View file

@ -14,15 +14,75 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "panic.h"
#include "di.h"
#include "pmc.h"
#include "fuse.h"
#include "utils.h"
#include "fs_utils.h"
#include "lib/log.h"
static uint32_t g_panic_code = 0;
static const char *get_error_desc_str(uint32_t error_desc) {
switch (error_desc) {
case 0x100:
return "Instruction Abort";
case 0x101:
return "Data Abort";
case 0x102:
return "PC Misalignment";
case 0x103:
return "SP Misalignment";
case 0x104:
return "Trap";
case 0x106:
return "SError";
case 0x301:
return "Bad SVC";
default:
return "Unknown";
}
}
static void _check_and_display_atmosphere_fatal_error(void) {
/* Check for valid magic. */
if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC) {
return;
}
{
/* Copy fatal error context to the stack. */
atmosphere_fatal_error_ctx ctx = *(ATMOSPHERE_FATAL_ERROR_CONTEXT);
/* Change magic to invalid, to prevent double-display of error/bootlooping. */
ATMOSPHERE_FATAL_ERROR_CONTEXT->magic = 0xCCCCCCCC;
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "A fatal error occurred when running Atmosph\xe8re.\n");
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Title ID: %016llx\n", ctx.title_id);
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Error Desc: %s (0x%x)\n", get_error_desc_str(ctx.error_desc), ctx.error_desc);
/* Save context to the SD card. */
{
char filepath[0x40];
snprintf(filepath, sizeof(filepath) - 1, "/atmosphere/fatal_errors/report_%016llx.bin", ctx.report_identifier);
filepath[sizeof(filepath)-1] = 0;
write_to_file(&ctx, sizeof(ctx), filepath);
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX,"Report saved to %s\n", filepath);
}
/* Display error. */
print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX,"\nPress POWER to reboot\n");
}
wait_for_button_and_reboot();
}
void check_and_display_panic(void) {
/* Handle a panic sent via a stratosphere module. */
_check_and_display_atmosphere_fatal_error();
/* We also handle our own panics. */
/* In the case of our own panics, we assume that the display has already been initialized. */
bool has_panic = APBDEV_PMC_RST_STATUS_0 != 0 || g_panic_code != 0;

View file

@ -28,6 +28,35 @@
#define PANIC_CODE_SAFEMODE 0x00000020
/* Atmosphere reboot-to-fatal-error. */
typedef struct {
uint32_t magic;
uint32_t error_desc;
uint64_t title_id;
union {
uint64_t gprs[32];
struct {
uint64_t _gprs[29];
uint64_t fp;
uint64_t lr;
uint64_t sp;
};
};
uint64_t pc;
uint64_t padding;
uint32_t pstate;
uint32_t afsr0;
uint32_t afsr1;
uint32_t esr;
uint64_t far;
uint64_t report_identifier; /* Normally just system tick. */
} atmosphere_fatal_error_ctx;
/* "AFE0" */
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x30454641
#define ATMOSPHERE_FATAL_ERROR_CONTEXT ((volatile atmosphere_fatal_error_ctx *)(0x4003E000))
void check_and_display_panic(void);
__attribute__ ((noreturn)) void panic(uint32_t code);

View file

@ -71,6 +71,7 @@
"svcMapDeviceAddressSpaceAligned": "0x5a",
"svcUnmapDeviceAddressSpace": "0x5c",
"svcGetSystemInfo": "0x6f",
"svcManageNamedPort": "0x71",
"svcCallSecureMonitor": "0x7F"
}
}

View file

@ -38,8 +38,23 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = 0x010041544D530000ul;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx) {
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
Utils::RebootToFatalError(ctx);
}
void __libnx_initheap(void) {
void* addr = nx_inner_heap;

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "bpc_ams_service.hpp"
#include "bpcmitm_reboot_manager.hpp"
Result BpcAtmosphereService::RebootToFatalError(InBuffer<AtmosphereFatalErrorContext> ctx) {
if (ctx.buffer == nullptr || ctx.num_elements != 1) {
return ResultKernelConnectionClosed;
}
/* Reboot to fusee with the input context. */
BpcRebootManager::RebootForFatalError(ctx.buffer);
return ResultSuccess;
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
enum BpcAtmosphereCmd : u32 {
BpcAtmosphereCmd_RebootToFatalError = 65000,
};
class BpcAtmosphereService : public IServiceObject {
private:
Result RebootToFatalError(InBuffer<AtmosphereFatalErrorContext> ctx);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MakeServiceCommandMeta<BpcAtmosphereCmd_RebootToFatalError, &BpcAtmosphereService::RebootToFatalError>(),
};
};

View file

@ -25,7 +25,7 @@ enum BpcCmd : u32 {
BpcCmd_RebootSystem = 1,
};
class BpcMitmService : public IMitmServiceObject {
class BpcMitmService : public IMitmServiceObject {
public:
BpcMitmService(std::shared_ptr<Service> s, u64 pid) : IMitmServiceObject(s, pid) {
/* ... */

View file

@ -25,6 +25,7 @@
#include "bpcmitm_main.hpp"
#include "bpc_mitm_service.hpp"
#include "bpc_ams_service.hpp"
#include "bpcmitm_reboot_manager.hpp"
#include "../utils.hpp"
@ -46,6 +47,10 @@ void BpcMitmMain(void *arg) {
}
AddMitmServerToManager<BpcMitmService>(server_manager, service_name, 13);
/* Extension: Allow for reboot-to-error. */
/* Must be managed port in order for sm to be able to access. */
server_manager->AddWaitable(new ManagedPortServer<BpcAtmosphereService>("bpc:ams", 1));
/* Loop forever, servicing our services. */
server_manager->Process();

View file

@ -64,8 +64,8 @@ static void ClearIram() {
memset(g_work_page, 0xFF, sizeof(g_work_page));
/* Overwrite all of IRAM with FFs. */
for (size_t ofs = 0; ofs < IRAM_PAYLOAD_MAX_SIZE; ofs += sizeof(g_work_page)) {
CopyToIram(IRAM_PAYLOAD_BASE + ofs, g_work_page, sizeof(g_work_page));
for (size_t ofs = 0; ofs < IRAM_SIZE; ofs += sizeof(g_work_page)) {
CopyToIram(IRAM_BASE + ofs, g_work_page, sizeof(g_work_page));
}
}
@ -99,3 +99,24 @@ Result BpcRebootManager::PerformReboot() {
return ResultSuccess;
}
}
void BpcRebootManager::RebootForFatalError(AtmosphereFatalErrorContext *ctx) {
/* If we don't actually have a payload loaded, just go to RCM. */
if (!g_payload_loaded) {
RebootToRcm();
}
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) {
CopyToIram(IRAM_PAYLOAD_BASE + ofs, &g_reboot_payload[ofs], 0x1000);
}
memcpy(g_work_page, ctx, sizeof(*ctx));
CopyToIram(IRAM_PAYLOAD_BASE + IRAM_PAYLOAD_MAX_SIZE, g_work_page, sizeof(g_work_page));
RebootToIramPayload();
}

View file

@ -18,7 +18,9 @@
#include <switch.h>
#include <stratosphere.hpp>
#define IRAM_PAYLOAD_MAX_SIZE 0x2F000
#define IRAM_BASE 0x40000000ull
#define IRAM_SIZE 0x40000
#define IRAM_PAYLOAD_MAX_SIZE 0x2E000
#define IRAM_PAYLOAD_BASE 0x40010000ull
enum class BpcRebootType : u32 {
@ -31,4 +33,5 @@ class BpcRebootManager {
public:
static void Initialize();
static Result PerformReboot();
static void RebootForFatalError(AtmosphereFatalErrorContext *ctx);
};

View file

@ -25,6 +25,7 @@
#include "ini.h"
#include "set_mitm/setsys_settings_items.hpp"
#include "bpc_mitm/bpcmitm_reboot_manager.hpp"
static FsFileSystem g_sd_filesystem = {0};
static HosSignal g_sd_signal;
@ -652,3 +653,7 @@ Result Utils::GetSettingsItemBooleanValue(const char *name, const char *key, boo
}
return rc;
}
void Utils::RebootToFatalError(AtmosphereFatalErrorContext *ctx) {
BpcRebootManager::RebootForFatalError(ctx);
}

View file

@ -87,6 +87,9 @@ class Utils {
static Result GetSettingsItemValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size);
static Result GetSettingsItemBooleanValue(const char *name, const char *key, bool *out);
/* Error occurred. */
static void RebootToFatalError(AtmosphereFatalErrorContext *ctx);
private:
static void RefreshConfiguration();
};

View file

@ -49,6 +49,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Boot;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}
void __libnx_initheap(void) {

View file

@ -136,7 +136,7 @@ bool CodeList::TryFindCodeRegion(Handle debug_handle, u64 guess, u64 *address) {
return true;
}
guess -= 4;
guess = mi.addr - 4;
}
return false;
}

View file

@ -38,6 +38,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Creport;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}

View file

@ -40,6 +40,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Dmnt;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}

View file

@ -35,6 +35,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Eclct;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}

View file

@ -45,6 +45,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Fatal;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}

@ -1 +1 @@
Subproject commit 1f9e2d042cb028cee1777b3d63a7cda06d2cffd0
Subproject commit b9724cdcadd5ea5fbead8f1a9c9b7de11daf6b60

View file

@ -41,6 +41,16 @@ extern "C" {
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Loader;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}
@ -103,9 +113,9 @@ struct LoaderServerOptions {
int main(int argc, char **argv)
{
consoleDebugInit(debugDevice_SVC);
auto server_manager = new WaitableManager<LoaderServerOptions>(1);
/* Add services to manager. */
server_manager->AddWaitable(new ServiceServer<ProcessManagerService>("ldr:pm", 1));
server_manager->AddWaitable(new ServiceServer<ShellService>("ldr:shel", 3));

View file

@ -117,8 +117,15 @@ Result NpdmUtils::LoadNpdmInternal(FILE *f_npdm, NpdmUtils::NpdmCache *cache) {
return rc;
}
if (info->header->mmu_flags > 0xF) {
return rc;
/* 7.0.0 added 0x10 as a valid bit to NPDM flags. */
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) {
if (info->header->mmu_flags > 0x1F) {
return rc;
}
} else {
if (info->header->mmu_flags > 0xF) {
return rc;
}
}
if (info->header->aci0_offset < sizeof(NpdmUtils::NpdmHeader) || info->header->aci0_size < sizeof(NpdmUtils::NpdmAci0) || info->header->aci0_offset + info->header->aci0_size > npdm_size) {

View file

@ -42,6 +42,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Pm;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}

View file

@ -39,6 +39,17 @@ extern "C" {
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
u64 __stratosphere_title_id = TitleId_Sm;
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}
@ -73,7 +84,7 @@ int main(int argc, char **argv)
/* TODO: What's a good timeout value to use here? */
auto server_manager = new WaitableManager(1);
/* Create sm:, (and thus allow things to register to it). */
server_manager->AddWaitable(new ManagedPortServer<UserService>("sm:", 0x40));