/* * 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 . */ #include #include #include #include #include #include #include #include #include #include "pm_boot2.hpp" #include "pm_registration.hpp" #include "pm_boot_mode.hpp" static std::vector g_launched_titles; static bool IsHexadecimal(const char *str) { while (*str) { if (isxdigit(*str)) { str++; } else { return false; } } return true; } static bool HasLaunchedTitle(u64 title_id) { return std::find(g_launched_titles.begin(), g_launched_titles.end(), title_id) != g_launched_titles.end(); } static void SetLaunchedTitle(u64 title_id) { g_launched_titles.push_back(title_id); } static void ClearLaunchedTitles() { g_launched_titles.clear(); } static void LaunchTitle(u64 title_id, FsStorageId storage_id, u32 launch_flags, u64 *pid) { u64 local_pid = 0; /* Don't launch a title twice during boot2. */ if (HasLaunchedTitle(title_id)) { return; } switch (Registration::LaunchProcessByTidSid(Registration::TidSid{title_id, storage_id}, launch_flags, &local_pid)) { case ResultKernelResourceExhausted: /* Out of resource! */ std::abort(); case ResultKernelOutOfMemory: /* Out of memory! */ std::abort(); case ResultKernelLimitReached: /* Limit Reached! */ std::abort(); default: /* We don't care about other issues. */ break; } if (pid) { *pid = local_pid; } SetLaunchedTitle(title_id); } static bool GetGpioPadLow(GpioPadName pad) { GpioPadSession button; if (R_FAILED(gpioOpenSession(&button, pad))) { return false; } /* Ensure we close even on early return. */ ON_SCOPE_EXIT { gpioPadClose(&button); }; /* Set direction input. */ gpioPadSetDirection(&button, GpioDirection_Input); GpioValue val; return R_SUCCEEDED(gpioPadGetValue(&button, &val)) && val == GpioValue_Low; } static bool IsMaintenanceMode() { /* Contact set:sys, retrieve boot!force_maintenance. */ DoWithSmSession([&]() { R_ASSERT(setsysInitialize()); }); { ON_SCOPE_EXIT { setsysExit(); }; u8 force_maintenance = 1; setsysGetSettingsItemValue("boot", "force_maintenance", &force_maintenance, sizeof(force_maintenance)); if (force_maintenance != 0) { return true; } } /* Contact GPIO, read plus/minus buttons. */ DoWithSmSession([&]() { R_ASSERT(gpioInitialize()); }); { ON_SCOPE_EXIT { gpioExit(); }; return GetGpioPadLow(GpioPadName_ButtonVolUp) && GetGpioPadLow(GpioPadName_ButtonVolDown); } return false; } static const std::tuple g_additional_launch_programs[] = { {TitleId_Am, true}, /* am */ {TitleId_NvServices, true}, /* nvservices */ {TitleId_NvnFlinger, true}, /* nvnflinger */ {TitleId_Vi, true}, /* vi */ {TitleId_Ns, true}, /* ns */ {TitleId_LogManager, true}, /* lm */ {TitleId_Ppc, true}, /* ppc */ {TitleId_Ptm, true}, /* ptm */ {TitleId_Hid, true}, /* hid */ {TitleId_Audio, true}, /* audio */ {TitleId_Lbl, true}, /* lbl */ {TitleId_Wlan, true}, /* wlan */ {TitleId_Bluetooth, true}, /* bluetooth */ {TitleId_BsdSockets, true}, /* bsdsockets */ {TitleId_Nifm, true}, /* nifm */ {TitleId_Ldn, true}, /* ldn */ {TitleId_Account, true}, /* account */ {TitleId_Friends, false}, /* friends */ {TitleId_Nfc, true}, /* nfc */ {TitleId_JpegDec, true}, /* jpegdec */ {TitleId_CapSrv, true}, /* capsrv */ {TitleId_Ssl, true}, /* ssl */ {TitleId_Nim, true}, /* nim */ {TitleId_Bcat, false}, /* bcat */ {TitleId_Erpt, true}, /* erpt */ {TitleId_Es, true}, /* es */ {TitleId_Pctl, true}, /* pctl */ {TitleId_Btm, true}, /* btm */ {TitleId_Eupld, false}, /* eupld */ {TitleId_Glue, true}, /* glue */ /* {TitleId_Eclct, true}, */ /* eclct */ /* Skip launching error collection in Atmosphere to lessen telemetry. */ {TitleId_Npns, false}, /* npns */ {TitleId_Fatal, true}, /* fatal */ {TitleId_Ro, true}, /* ro */ {TitleId_Profiler, true}, /* profiler */ {TitleId_Sdb, true}, /* sdb */ {TitleId_Migration, true}, /* migration */ {TitleId_Grc, true}, /* grc */ {TitleId_Olsc, true}, /* olsc */ }; static void MountSdCard() { DoWithSmSession([&]() { Handle tmp_hnd = 0; static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:c"}; for (unsigned int i = 0; i < sizeof(required_active_services) / sizeof(required_active_services[0]); i++) { R_ASSERT(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i]))); svcCloseHandle(tmp_hnd); } }); fsdevMountSdmc(); } static void WaitForMitm(const char *service) { const auto name = sts::sm::ServiceName::Encode(service); while (true) { bool mitm_installed = false; R_ASSERT(sts::sm::manager::HasMitm(&mitm_installed, name)); if (mitm_installed) { break; } svcSleepThread(1000000ull); } } void EmbeddedBoot2::Main() { /* Wait until fs.mitm has installed itself. We want this to happen as early as possible. */ WaitForMitm("fsp-srv"); /* Clear titles. */ ClearLaunchedTitles(); /* psc, bus, pcv is the minimal set of required titles to get SD card. */ /* bus depends on pcie, and pcv depends on settings. */ /* Launch psc. */ LaunchTitle(TitleId_Psc, FsStorageId_NandSystem, 0, NULL); /* Launch pcie. */ LaunchTitle(TitleId_Pcie, FsStorageId_NandSystem, 0, NULL); /* Launch bus. */ LaunchTitle(TitleId_Bus, FsStorageId_NandSystem, 0, NULL); /* Launch settings. */ LaunchTitle(TitleId_Settings, FsStorageId_NandSystem, 0, NULL); /* Launch pcv. */ LaunchTitle(TitleId_Pcv, FsStorageId_NandSystem, 0, NULL); /* At this point, the SD card can be mounted. */ MountSdCard(); /* Find out whether we are maintenance mode. */ bool maintenance = IsMaintenanceMode(); if (maintenance) { BootModeService::SetMaintenanceBootForEmbeddedBoot2(); } /* Wait for other atmosphere mitm modules to initialize. */ WaitForMitm("set:sys"); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { WaitForMitm("bpc"); } else { WaitForMitm("bpc:c"); } /* Launch usb. */ LaunchTitle(TitleId_Usb, FsStorageId_NandSystem, 0, NULL); /* Launch tma. */ LaunchTitle(TitleId_Tma, FsStorageId_NandSystem, 0, NULL); /* Launch Atmosphere dmnt, using FsStorageId_None to force SD card boot. */ LaunchTitle(TitleId_Dmnt, FsStorageId_None, 0, NULL); /* Launch default programs. */ for (auto &launch_program : g_additional_launch_programs) { if (!maintenance || std::get(launch_program)) { LaunchTitle(std::get(launch_program), FsStorageId_NandSystem, 0, NULL); } /* In 7.0.0, Npns was added to the list of titles to launch during maintenance. */ if (maintenance && std::get(launch_program) == TitleId_Npns && GetRuntimeFirmwareVersion() >= FirmwareVersion_700) { LaunchTitle(TitleId_Npns, FsStorageId_NandSystem, 0, NULL); } } /* Allow for user-customizable programs. */ DIR *titles_dir = opendir("sdmc:/atmosphere/titles"); struct dirent *ent; if (titles_dir != NULL) { while ((ent = readdir(titles_dir)) != NULL) { if (strlen(ent->d_name) == 0x10 && IsHexadecimal(ent->d_name)) { u64 title_id = (u64)strtoul(ent->d_name, NULL, 16); if (HasLaunchedTitle(title_id)) { continue; } char title_path[FS_MAX_PATH] = {0}; strcpy(title_path, "sdmc:/atmosphere/titles/"); strcat(title_path, ent->d_name); strcat(title_path, "/flags/boot2.flag"); FILE *f_flag = fopen(title_path, "rb"); if (f_flag != NULL) { fclose(f_flag); LaunchTitle(title_id, FsStorageId_None, 0, NULL); } else { /* TODO: Deprecate this in the future. */ memset(title_path, 0, FS_MAX_PATH); strcpy(title_path, "sdmc:/atmosphere/titles/"); strcat(title_path, ent->d_name); strcat(title_path, "/boot2.flag"); f_flag = fopen(title_path, "rb"); if (f_flag != NULL) { fclose(f_flag); LaunchTitle(title_id, FsStorageId_None, 0, NULL); } } } } closedir(titles_dir); } /* We no longer need the SD card. */ fsdevUnmountAll(); /* Free the memory used to track what boot2 launches. */ ClearLaunchedTitles(); }