Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configs/boot-args.lst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ cp_if_kb k
cp_nv k
cp_ping k
cp_rev k
cp_rfs k
cp_route k
cp_sim k
cp_vss k
Expand Down
1 change: 1 addition & 0 deletions configs/symbols.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ CP_IF_KB arg cp_if_kb User space k hakios.shannon.c with cp_if=1: at most N KB t
CP_NV arg cp_nv User space k hakios.shannon.c the phone's own NV data, a file nv_data.bin the image's builder put beside the kernel, is given to the modem (none: zeros, and no stage 4)
CP_PING arg cp_ping User space k hakios.shannon.c the address stage 7 sends its one echo request to (none: the context's first DNS server)
CP_REV arg cp_rev User space k hakios.shannon.c the board's revision told to the modem (26, this phone's)
CP_RFS arg cp_rfs User space k hakios.shannon.c an OPEN FILE answered from RAM: the file is not there
CP_ROUTE arg cp_route User space k hakios.shannon.c with cp_if=1: an IPv4 default route through pdp_ip0 (only when there is none yet: Wi-Fi's wins)
CP_SIM arg cp_sim User space k hakios.shannon.c in low power mode, once the SIM is ready, one read of its administrative data file (EF_AD, 4 bytes: no identity) is asked, to see if the modem takes
CP_VSS arg cp_vss User space k hakios.shannon.c the image's VSS part (the voice processor's) is not copied
Expand Down
5 changes: 3 additions & 2 deletions docs/boot-args.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Made by `tools/boot-args.py --markdown`; CI fails if it is out of date with the source (`tools/boot-args.py --check`).

Every argument that the kernel, the loader or user space of this port reads, and which file reads it. **688 arguments**: 597 read by the kernel, 23 by the loader, 85 by user space (one is read in more than one place).
Every argument that the kernel, the loader or user space of this port reads, and which file reads it. **689 arguments**: 598 read by the kernel, 23 by the loader, 85 by user space (one is read in more than one place).

This is a catalogue of what exists and who reads it, not a manual. What an argument means is in the head of the file that reads it. The stable switches have a symbol in the configuration (`tools/hakios-config.py help SYMBOL`, `docs/configs.md`); the rest are the knobs of bring-up (a driver's steps, pauses, traces, test modes) and can change with the driver. Any of them can be given in a configuration with `CONFIG_BOOTARGS="name=value ..."`, which checks that each name is in this list. What the arguments of the two Metal paths do: [Metal on the Adreno](reference/metal.md#boot-arguments).

Expand Down Expand Up @@ -593,7 +593,7 @@ Layers: **K** the kernel, **L** the loader, **U** user space (through `sysctl ke
| `ufs_probe` | L | `loader.c` | `CONFIG_UFS_PROBE` | |
| `ufs_write_guid` | L | `loader.c` | `CONFIG_UFS_WRITE_GUID` | |

## User space (183)
## User space (184)

| Argument | Layer | Read by | Configuration | Set by |
|---|---|---|---|---|
Expand Down Expand Up @@ -668,6 +668,7 @@ Layers: **K** the kernel, **L** the loader, **U** user space (through `sysctl ke
| `cp_nv` | K | `hakios.shannon.c` | `CONFIG_CP_NV` | |
| `cp_ping` | K | `hakios.shannon.c` | `CONFIG_CP_PING` | |
| `cp_rev` | K | `hakios.shannon.c` | `CONFIG_CP_REV` | |
| `cp_rfs` | K | `hakios.shannon.c` | `CONFIG_CP_RFS` | |
| `cp_route` | K | `hakios.shannon.c` | `CONFIG_CP_ROUTE` | |
| `cp_sim` | K | `hakios.shannon.c` | `CONFIG_CP_SIM` | |
| `cp_vss` | K | `hakios.shannon.c` | `CONFIG_CP_VSS` | |
Expand Down
37 changes: 37 additions & 0 deletions docs/ios27/MODEM-IOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ binary, offset or table is in this file.
| `cp_if=1` | The active data context becomes `pdp_ip0`: IPv4, point to point, with the context's address on it. The context stays up until `sysctl kern.hakios.cp.go=2` |
| `cp_route=1` | An IPv4 default route through `pdp_ip0`, if there is none yet |
| `cp_if_kb=N` | A per-boot byte budget on `pdp_ip0`, 2048 KB by default |
| `cp_rfs=1` | With `cp_nv=2`: the CP's file opens (RFS command 0x11, e.g. `CP_OMC_AUDIO_SLSI.bin`) are answered "not there" (handle -1, errno 2) from RAM. Nothing is read or opened |
| (always) | `kern.hakios.cp.info`: one line for user space (stage, registration, technology, bars, dBm, operator code, SIM, data, bytes used, budget, a change counter). `kern.hakios.cp.addr` (root only): the context's address and DNS servers |
| (with `cp_if=1`) | Idle polling in the steady state (1 ms while anything moves, 10 ms after 0.3 s quiet, 50 ms after 5 s); a lost registration gets 10 minutes to come back; the modem is put in low power and reset on an iOS shutdown or restart (xnu `PEHaltRestartInternal`) |

User space (`tools/ios27/modem/`, built by `build.sh`, installed by `66-celld.sh`):

| Piece | What it does |
|---|---|
| `scion-celld` | Root daemon (job `com.scion.celld`). While the context is up it publishes `State:/Network/Service/<id>` (`PrimaryRank` Last), `.../IPv4` (address, `InterfaceName` pdp_ip0, `Router` = the address) and `.../DNS` (the context's servers) to configd; withdraws them when it goes. Exits at once on a boot without the modem |
| `scion-bars` (`c.dylib`) | Injected into SpringBoard (`sweetinject=...;/usr/lib/c.dylib,SpringBoard`), on only while `/private/var/local/scion-bars` exists: the status bar's cellular entry (bars, the network's name) from `kern.hakios.cp.info` |
| `scion-fetch`, job `com.scion.celltest` | Test roots only: one https GET by name through iOS's own stack, logged with configd's view and the routes |

Seen on phone 1 (SM-G960F, GoMo SIM on eir, 272-03), 13 Nov 2026:
- SIM ready, registered HOME.
Expand Down Expand Up @@ -82,3 +93,29 @@ What stays out of every path:
(step 1 above).
3. Then the status-bar library (step 2).
4. Then decide on step 3 with the owner.

## The CommCenter shim (plan only; not started: needs the owner's go)

What a real CoreTelephony baseband would take, written down so that the decision can be made on facts:

1. **Find what CommCenter opens.** On the iPhone12,1 identity CommCenter expects an Intel baseband: IOKit services of
Apple's baseband drivers (the PCIe "ARI" transport and its user client), a boot/firmware path (BBUpdater), and the
ARI protocol once the modem is up. On this port none of these services exist, so CommCenter decides there is no
baseband. The first step is read-only: boot with CommCenter's own logging and list every IOKit service, XPC service
and file it looks for (no code).
2. **Two shapes, both large:**
- *An ARI baseband in the kernel:* a fake transport service (our own IOService with the matching names and a user
client) that answers ARI messages for the few groups CommCenter needs to say "SIM ready, registered, signal N,
data context up", translating to and from Samsung IPC (`hakios.shannon.c` already speaks the SIM status,
registration, signal and the data context). ARI's message set for this firmware generation is public only in
part (ARIstoteles, WiSec 2021); every message would have to be learned from CommCenter's behaviour, never from
Apple binaries copied into the tree.
- *A replacement for CommCenter's XPC services:* our own daemon answering `CTServerConnection` clients. The
interface is large and private; Settings, SpringBoard and many daemons use it.
3. **What it would give:** SIM in Settings, the operator from the carrier bundle, bars everywhere, data through iOS's own
path (CommCenter makes `pdp_ip` and the configd service itself, replacing `scion-celld`).
4. **Limits that stay:** no IMEI or NV change, no unlock, no calls or SMS without the owner's say-so, EFS read-only,
no Apple binary patched.
5. **Smallest useful first step if the owner says go:** step 1 (the list), then a kernel-only stub service that makes
CommCenter log "baseband found" and nothing more, behind a boot-arg.

113 changes: 108 additions & 5 deletions drivers/modem/hakios.shannon.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@
* cp_if_kb=N with cp_if=1: at most N KB through pdp_ip0 in a boot, both ways (2048 by default; the owner's mobile plan), then it drops
* cp_route=1 with cp_if=1: an IPv4 default route through pdp_ip0 (only when there is none yet: Wi-Fi's wins)
* cp_nv=2 the phone's own NV data read at boot from the EFS partition (/nv_data.bin, read-only, never mounted, never written), kept in RAM only; the CP's file requests for it (RFS NV read/write) are answered from that copy and its writes kept there for the run, never written back
* cp_rfs=1 with cp_nv=2: the CP's file opens (RFS command 0x11, e.g. CP_OMC_AUDIO_SLSI.bin) answered "not there" (handle -1,
* ENOENT) from RAM; nothing is read or opened. Off: they stay unanswered
* cp_vss=0 the image's VSS part (the voice processor's) is not copied
* cp_rev=N the board's revision told to the modem (26, this phone's)
* cp_ds=N the dual SIM field told to the modem (0)
Expand All @@ -154,6 +156,7 @@
#include <sys/hakios.input.h>
#include <sys/hakios.log.h>
#include <sys/hakios.physmap.h>
#include <sys/kauth.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/hakios.xnu.h>
Expand Down Expand Up @@ -354,6 +357,14 @@ static struct {
uint32_t if_on, route_on; /* cp_if=1, cp_route=1 */
uint32_t if_kb; /* cp_if_kb=N */
uint32_t dropped[NRINGS]; /* such frames dropped */
/* the modem's state for user space (kern.hakios.cp.info; kern.hakios.cp.addr for root): no identity in any of it */
uint8_t dns4b[4]; /* the context's second DNS server (never logged) */
int tech, bars, dbm; /* the registration's technology, the icon's bars, the strength in dBm; -1/0 none yet */
char oper[8]; /* the serving network's operator code (country + network: the same for everyone on it) */
uint32_t gen; /* bumped on every change of the above */
uint32_t rfs_open, rfs_opens; /* cp_rfs=1: the CP's file opens answered "not there" (from RAM: nothing is read) */
uint64_t busy_ms; /* the last time something came from the CP or waited to go to it */
uint64_t lost_ms; /* when a registration was lost (0: registered, or never was) */
} cp;

static int cp_go;
Expand Down Expand Up @@ -1243,6 +1254,10 @@ fmt_received(const uint8_t *m, uint32_t n)
/* the address and the first DNS server, for stage 7: used only if both look like host addresses */
memcpy(cp.ip4, m + 10, 4);
memcpy(cp.dns4, m + 14, 4);
if (n >= 7 + 3 + 12) {
memcpy(cp.dns4b, m + 18, 4);
}
cp.gen++;
cp.have4 = cp.ip4[0] != 0 && cp.ip4[0] != 127 && cp.ip4[0] < 224 && cp.dns4[0] != 0 &&
cp.dns4[0] != 127 && cp.dns4[0] < 224;
if (!cp.ping_given) {
Expand Down Expand Up @@ -1288,7 +1303,12 @@ fmt_received(const uint8_t *m, uint32_t n)
if (m[9] != (uint8_t)cp.reg_state) {
cp.reg_changes++;
}
if ((m[9] == 2 || m[9] == 6) != (cp.reg_state == 2 || cp.reg_state == 6)) {
cp.lost_ms = (m[9] == 2 || m[9] == 6) ? 0 : now_ms();
}
cp.reg_state = m[9];
cp.tech = m[7];
cp.gen++;
if ((m[9] == 2 || m[9] == 6) && cp.registered_ms == 0 && cp.normal_asked) {
cp.registered_ms = now_ms();
say("cp: REGISTERED %llu ms after normal mode was asked for\n", cp.registered_ms - cp.normal_ms);
Expand All @@ -1315,10 +1335,20 @@ fmt_received(const uint8_t *m, uint32_t n)
}
}
code[k] = 0;
if (k != 0) {
strlcpy(cp.oper, code, sizeof(cp.oper));
cp.gen++;
}
say("cp: SERVING NETWORK: operator code %s (of %u bytes)\n", k != 0 ? code : "not found in it", n - 7);
} else if (group == 0x07 && (index == 0x01 || index == 0x06) && n >= 7 + 4) {
/* signal: the display group's icon (number 1) and strength (number 6) words: levels, no identity */
cp.signals++;
if (index == 1 && (m[7] & 1) != 0) {
cp.bars = m[8]; /* the icon's flags (bit 0: the signal's part is valid), then its bars */
} else if (index == 6) {
cp.dbm = -(int)m[7]; /* the strength report: minus dBm */
}
cp.gen++;
if (cp.signals <= 6 || cp.signals % 10 == 0) {
say("cp: SIGNAL (%s, the %uth): %02x %02x %02x %02x %02x %02x\n", index == 1 ? "icons" : "strength",
cp.signals, m[7], m[8], m[9], m[10], n > 11 ? m[11] : 0, n > 12 ? m[12] : 0);
Expand Down Expand Up @@ -1473,8 +1503,20 @@ rfs_received(const uint8_t *m, uint32_t n)
cp.rfs_requests, cmd, id, len);
return;
}
say("cp: FILE REQUEST %u from the CP (not answered): command %#x, id %u, %u bytes in all\n", cp.rfs_requests, cmd, id,
len);
if (cmd == 0x11 && cp.rfs_open != 0 && cp.nv_ram != NULL) {
/* cp_rfs=1: an OPEN FILE answered from RAM: the file is not there. The confirmation is the RFS header and two
* 32-bit words, the handle (-1) and errno (2, ENOENT): 14 bytes, as Samsung's file server sends it. Nothing is
* read or opened anywhere */
static const int32_t none[2] = { -1, 2 };

cp.rfs_opens++;
(void)rfs_answer(0x11, id, (const uint8_t *)none, sizeof(none));
say("cp: FILE REQUEST %u from the CP: OPEN FILE (id %u, %u bytes): answered \"not there\" (ENOENT), the %uth\n",
cp.rfs_requests, id, len, cp.rfs_opens);
} else {
say("cp: FILE REQUEST %u from the CP (not answered): command %#x, id %u, %u bytes in all\n", cp.rfs_requests, cmd,
id, len);
}
/* a path, if the request starts with one after a few words: printable text of 4 characters or more with a '/' or '.' */
for (uint32_t i = 6; i + 4 < n && i < 64; i++) {
uint32_t k = i;
Expand Down Expand Up @@ -1829,6 +1871,50 @@ pdp_attach(void)
}
}

/* kern.hakios.cp.info: the modem's state as one line for user space (scion-celld, the status bar's library):
* registration, technology, bars, dBm, operator code, SIM status, phone state, data (1: pdp_ip0 up with its address),
* bytes through pdp_ip0 this boot, the budget, and a counter that moves on every change. No identity in it. */
static int
cp_info_sysctl SYSCTL_HANDLER_ARGS
{
#pragma unused(oidp, arg1, arg2)
char line[200];

snprintf(line, sizeof(line), "stage=%u state=%u reg=%d tech=%d bars=%d dbm=%d oper=%s sim=%d phone=%d data=%d used=%llu "
"budget=%llu gen=%u", cp.stage, cp.state, cp.reg_state, cp.tech, cp.bars, cp.dbm, cp.oper[0] != 0 ? cp.oper : "-",
cp.sim_status, cp.phone_state, pdp.addressed && cp.data_step == 3 ? 1 : 0, pdp.in_bytes + pdp.out_bytes,
(uint64_t)cp.if_kb * 1024, cp.gen);
return SYSCTL_OUT(req, line, strlen(line) + 1);
}

/* kern.hakios.cp.addr: "ADDRESS DNS1 DNS2" of the active context, root only (empty while there is none): what
* scion-celld gives configd. Never logged. */
static int
cp_addr_sysctl SYSCTL_HANDLER_ARGS
{
#pragma unused(oidp, arg1, arg2)
char line[64] = "";

if (req->newptr != USER_ADDR_NULL) {
return EPERM;
}
if (!kauth_cred_issuser(kauth_cred_get())) {
return EPERM;
}
if (pdp.addressed && cp.have4 && cp.data_step == 3) {
snprintf(line, sizeof(line), "%u.%u.%u.%u %u.%u.%u.%u %u.%u.%u.%u", cp.ip4[0], cp.ip4[1], cp.ip4[2], cp.ip4[3],
cp.dns4[0], cp.dns4[1], cp.dns4[2], cp.dns4[3], cp.dns4b[0], cp.dns4b[1], cp.dns4b[2], cp.dns4b[3]);
}
int e = SYSCTL_OUT(req, line, strlen(line) + 1);
bzero(line, sizeof(line));
return e;
}

SYSCTL_PROC(_kern_hakios_cp, OID_AUTO, info, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_LOCKED, NULL, 0, cp_info_sysctl, "A",
"the modem's state for user space: registration, technology, bars, dBm, operator code, SIM, phone state, data");
SYSCTL_PROC(_kern_hakios_cp, OID_AUTO, addr, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_LOCKED, NULL, 0, cp_addr_sysctl, "A",
"the data context's address and DNS servers (root only; empty without one)");

static void
data_received(uint8_t ch, const uint8_t *p, uint32_t n)
{
Expand Down Expand Up @@ -2224,6 +2310,8 @@ poll_once(void)
if (pending != 0) {
uint16_t msg = (uint16_t)rd(cp.mbox, MBOX_ISSR(WORD_CP_MSG));

cp.busy_ms = now_ms();

wr(cp.mbox, MBOX_INTCR0, pending);
if (pending & (1u << (16 + IRQ_CP_MSG))) {
alive = handle_msg(msg);
Expand Down Expand Up @@ -2600,7 +2688,8 @@ cp_thread(__unused void *arg, __unused wait_result_t result)
back = "NOT registered 90 s after normal mode was asked for";
why5 = "not registered in time";
} else if (cp.registered_ms != 0 && cp.reg_state != 2 && cp.reg_state != 6 &&
now_ms() - cp.registered_ms > 5000) {
now_ms() - cp.registered_ms > 5000 && (!cp.if_on || now_ms() - cp.lost_ms > 600000)) {
/* (cp_if=1, the everyday mode: the CP looks for its network again by itself; given 10 minutes) */
back = "the registration was lost";
why5 = "registration lost";
} else if (cp.stop || go == 2) {
Expand Down Expand Up @@ -2662,7 +2751,19 @@ cp_thread(__unused void *arg, __unused wait_result_t result)
if (now_ms() >= until) {
break;
}
IOSleep(1);
if (cp.if_on && cp.data_step == 3 && pdp.ifp != NULL && cp.normal_asked && !cp.back_asked) {
/* the everyday steady state (cp_if=1, the context up): every 1 ms while anything moves, every 10 ms after
* 0.3 s of quiet, every 50 ms after 5 s (the mailbox is polled: its interrupts stay masked) */
uint64_t quiet;

if (pdp.head != pdp.tail) {
cp.busy_ms = now_ms();
}
quiet = now_ms() - cp.busy_ms;
IOSleep(quiet > 5000 ? 50 : quiet > 300 ? 10 : 1);
} else {
IOSleep(1);
}
}
if (cp.normal_asked) {
say("cp: STAGE 5 SUMMARY: normal mode asked; registered: %s; last registration state %d; %u changes of it; %u "
Expand Down Expand Up @@ -2698,7 +2799,7 @@ hakios_shannon_quiesce(void)
{
if (cp.started) {
cp_go = 2;
for (uint32_t i = 0; i < 300 && cp.started; i++) {
for (uint32_t i = 0; i < 500 && cp.started; i++) {
IOSleep(10);
}
}
Expand All @@ -2719,6 +2820,8 @@ hakios_shannon_init(void)
cp.wait_s = cp.stage >= 4 ? 25 : 20;
cp.delay_s = 30;
cp.phone_state = cp.sim_status = cp.reg_state = -1;
cp.tech = cp.bars = -1;
PE_parse_boot_argn("cp_rfs", &cp.rfs_open, sizeof(cp.rfs_open));
cp.vss_on = 1;
PE_parse_boot_argn("cp_nv", &cp.nv_file, sizeof(cp.nv_file));
PE_parse_boot_argn("cp_if", &cp.if_on, sizeof(cp.if_on));
Expand Down
2 changes: 1 addition & 1 deletion profiles/ios-27.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ profile ios-27
xnu_commit ac9718fb1af618d5ce8678d0dc6e8a58f252216f
xnu_fork https://github.com/Scion-Project/xnu.git
fork_branch scion/xnu-12377
fork_commit 0c9b2cd0e93f9c6aace51f0a392cb5bfeb2618dd
fork_commit 618114acd839cf950f52b781a5959d8d9082894a
fork_copy profiles/ios-27.copy
fork_stage xnu/stage/00_setup_coreentitlements.sh
drivers none
Expand Down
Loading
Loading