diff --git a/avf/qemu-scfb.sh b/avf/qemu-scfb.sh index c9f9ea5cf..6eb065b4b 100755 --- a/avf/qemu-scfb.sh +++ b/avf/qemu-scfb.sh @@ -8,6 +8,8 @@ # env: MEMG (default 4), EXTRA (more boot-args), HMP (monitor port, 4454), QMP (4455) # CAM=1: and the camera region (SCCAM, avf/CAMERA.md) 16 MB below the framebuffer's, with guest RAM a # shared file (RAMFILE, default a temporary one) that avf/sccam-host.py serves a test pattern into +# NET=1: a virtio-net-pci card on QEMU's user network, as Scion's Network setting gives one under +# AVF; with virtio_net_wifi=1, which this adds, iOS sees it as Wi-Fi. NET=wired: the same card as a cable # Note: under hvf, an extra virtio-tablet-pci made QEMU abort ("!s1ptw" in hvf.c); keep the device set as is. set -u HERE=$(cd "$(dirname "$0")" && pwd) @@ -18,6 +20,12 @@ MEMG=${MEMG:-4} IMAGE=$HERE/shim/out/Image [ -f "$IMAGE" ] || "$HERE/shim/build.sh" REGION=$(( 0x40000000 + (MEMG << 30) - (36 << 20) )) +NETARGS=() +case "${NET:-}" in +'' | 0) ;; +wired) NETARGS=(-netdev user,id=net0 -device virtio-net-pci,netdev=net0) ;; +*) NETARGS=(-netdev user,id=net0 -device virtio-net-pci,netdev=net0); EXTRA="virtio_net_wifi=1 ${EXTRA:-}" ;; +esac PARAMS="scfb.addr=$(printf 0x%x $REGION) scfb.size=0x2000000 scfb.w=1125 scfb.h=2436 md0=rvblk0 ${EXTRA:-} -v" RAM=(-m "${MEMG}G") if [ "${CAM:-0}" = 1 ]; then @@ -36,6 +44,6 @@ echo "scfb region $(printf 0x%x $REGION); boot-args: $PARAMS" >&2 timeout "$SECS" qemu-system-aarch64 -M virt,gic-version=3 -accel "${ACCEL:-hvf}" -cpu "${CPU:-host}" -smp 1 "${RAM[@]}" \ -kernel "$IMAGE" -initrd "$PAYLOAD" -append "$PARAMS" \ -drive "file=$SYSTEM,format=raw,if=none,id=sys,readonly=on" -device virtio-blk-pci,drive=sys \ - -device virtio-multitouch-pci \ + -device virtio-multitouch-pci ${NETARGS[@]+"${NETARGS[@]}"} \ -nographic -serial stdio -no-reboot \ -monitor "tcp:127.0.0.1:${HMP:-4454},server,nowait" -qmp "tcp:127.0.0.1:${QMP:-4455},server,nowait" diff --git a/avf/scion/avf.properties b/avf/scion/avf.properties index cb7f1d05a..d9281d301 100644 --- a/avf/scion/avf.properties +++ b/avf/scion/avf.properties @@ -8,6 +8,9 @@ kernel=Image initrd=payload.bin disk.0=system.apfs -params=md0=rvblk0 +# virtio_net_wifi=1: with Scion's "Network" setting on, AVF adds a virtio-net card (Android's VM +# tethering behind it) and iOS sees it as Wi-Fi, joined to the open network "Scion" +# (bsd/dev/redwood_virtio_net.c). Without the setting there is no card and it does nothing. +params=md0=rvblk0 virtio_net_wifi=1 memory_mib=4096 cpus=1 diff --git a/xnu/files/bsd/dev/redwood_apple80211.c b/xnu/files/bsd/dev/redwood_apple80211.c index e2674ee51..d23b79465 100644 --- a/xnu/files/bsd/dev/redwood_apple80211.c +++ b/xnu/files/bsd/dev/redwood_apple80211.c @@ -436,10 +436,18 @@ get(struct apple80211req *request) case 103: /* all of it, as a scan's result */ error = state->phase != REDWOOD_STA_JOINED ? ENXIO : give_network(request, &state->bss); break; - case 2: { /* how it was joined: open system, then WPA2 with a passphrase */ - struct { + case 2: { /* how it was joined: open system, then WPA2 with a passphrase -- or */ + struct { /* nothing more, for a network whose beacon has no RSN element (48) */ uint32_t version, lower, upper, extended; } answer = { .version = 1, .lower = 1, .upper = 8 }; + bool secured = false; + + for (unsigned at = 0; at + 2 <= state->bss.elements_length; at += 2 + state->bss.elements[at + 1]) { + secured |= state->bss.elements[at] == 48; + } + if (!secured && state->bss.elements_length != 0) { + answer.upper = 0; + } error = state->phase != REDWOOD_STA_JOINED ? ENXIO : give(request, &answer, sizeof(answer)); break; diff --git a/xnu/files/bsd/dev/redwood_virtio_net.c b/xnu/files/bsd/dev/redwood_virtio_net.c index 238441883..40a051b05 100644 --- a/xnu/files/bsd/dev/redwood_virtio_net.c +++ b/xnu/files/bsd/dev/redwood_virtio_net.c @@ -9,6 +9,14 @@ * Nothing of what the device offers is taken (no checksums or segmentation left to it, no * merged receive buffers): a 12-byte header nobody reads, then the frame, one buffer each. * No interrupt is used; the interface's thread asks (virtio_poll()). + * + * Boot-arg virtio_net_wifi=1: iOS is told that en0 is a radio, the way the phone's is + * (redwood_net.c, RedwoodWLAN.cpp, redwood_apple80211.c), and a station of this file's stands + * behind it (the station boundary, sys/redwood_sta.h). It hears one open network, "Scion" (or + * virtio_net_ssid=NAME), joins it at the start and whenever iOS asks, and then lets frames + * pass. So Settings shows Wi-Fi on and that network joined, configd configures the interface + * by itself (a "Wi-Fi" network service, DHCP), and the status bar has the Wi-Fi bars. Under + * AVF (Scion's "Network" setting) the cable behind it is Android's VM tethering. */ #include #include @@ -17,7 +25,9 @@ #include #include #include +#include #include +#include #include #include @@ -169,10 +179,170 @@ static struct redwood_net_link virtio_link = { .poll = virtio_poll, }; +/* ---- virtio_net_wifi=1: a station with one network, always in reach ------------------------------------- */ + +#define VSTA_FREQUENCY 2437 /* channel 6 */ +#define VSTA_RSSI (-45) + +static struct redwood_sta_bss vsta_bss; +static enum redwood_sta_phase vsta_phase = REDWOOD_STA_IDLE; +static int vsta_error; + +static void +vsta_link(bool up) +{ + redwood_net_link_state(up); +} + +static int +vsta_power(bool on) +{ + if (!on && vsta_phase == REDWOOD_STA_JOINED) { + vsta_link(false); + } + vsta_phase = on ? REDWOOD_STA_IDLE : REDWOOD_STA_OFF; + return 0; +} + +static int +vsta_scan(__unused const struct redwood_sta_scan *what) +{ + return vsta_phase == REDWOOD_STA_OFF ? ENETDOWN : 0; +} + +static void +vsta_heard(void (*each)(const struct redwood_sta_bss *bss, void *context), void *context) +{ + each(&vsta_bss, context); +} + +static int +vsta_join(const struct redwood_sta_join *what) +{ + bool ours = what->ssid_length == vsta_bss.ssid_length && memcmp(what->ssid, vsta_bss.ssid, what->ssid_length) == 0; + + if (vsta_phase == REDWOOD_STA_OFF) { + return ENETDOWN; + } + if (!ours) { + vsta_error = ENOENT; /* (nothing else is in reach) */ + return ENOENT; + } + if (what->security != REDWOOD_STA_OPEN) { + vsta_error = EAUTH; /* (it is an open network: a key is the wrong one) */ + return EAUTH; + } + vsta_error = 0; + if (vsta_phase != REDWOOD_STA_JOINED) { + vsta_phase = REDWOOD_STA_JOINED; + vsta_link(true); + } + return 0; +} + +static int +vsta_leave(void) +{ + if (vsta_phase == REDWOOD_STA_JOINED) { + vsta_link(false); + vsta_phase = REDWOOD_STA_IDLE; + } + return 0; +} + +static void +vsta_state(struct redwood_sta_state *state) +{ + state->phase = vsta_phase; + state->bss = vsta_bss; + state->last_error = vsta_error; +} + +static unsigned +vsta_channels(struct redwood_sta_channel *channels, unsigned most) +{ + static const uint16_t frequencies[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 5180, 5200, 5220, 5240 }; + unsigned count = 0; + + for (unsigned i = 0; i < sizeof(frequencies) / sizeof(frequencies[0]) && count < most; i++) { + channels[count++] = (struct redwood_sta_channel){ .frequency = frequencies[i] }; + } + return count; +} + +static struct redwood_sta_ops vsta_ops = { + .power = vsta_power, + .scan = vsta_scan, + .heard = vsta_heard, + .join = vsta_join, + .leave = vsta_leave, + .state = vsta_state, + .channels = vsta_channels, +}; + +/* The network: what a beacon of an open 802.11g access point on channel 6 would say. */ +static void +vsta_start(const uint8_t address[ETHER_ADDR_LEN]) +{ + static const uint8_t rates[] = { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24 }; /* 1 2 5.5 11 basic; 6 9 12 18 */ + static const uint8_t more_rates[] = { 0x30, 0x48, 0x60, 0x6c }; /* 24 36 48 54 */ + static struct redwood_sta_join join; + char name[33] = "Scion"; + struct redwood_sta_bss *b = &vsta_bss; + uint8_t *e = b->elements; + size_t length; + + PE_parse_boot_argn("virtio_net_ssid", name, sizeof(name)); + name[32] = 0; + length = strlen(name); + if (length == 0) { + strlcpy(name, "Scion", sizeof(name)); + length = 5; + } + memcpy(b->ssid, name, length); + b->ssid_length = (uint8_t)length; + /* the access point's address: ours with another last byte, and "locally administered" */ + memcpy(b->bssid, address, ETHER_ADDR_LEN); + b->bssid[0] = (uint8_t)((b->bssid[0] | 0x02) & ~0x01); + b->bssid[5] ^= 0x01; + b->frequency = VSTA_FREQUENCY; + b->rssi = VSTA_RSSI; + b->beacon_interval = 100; + b->capability = 0x0421; /* an access point (ESS), short preamble, short slot time; no privacy */ + *e++ = 0; /* SSID */ + *e++ = (uint8_t)length; + memcpy(e, name, length); + e += length; + *e++ = 1; /* supported rates */ + *e++ = sizeof(rates); + memcpy(e, rates, sizeof(rates)); + e += sizeof(rates); + *e++ = 3; /* the channel */ + *e++ = 1; + *e++ = 6; + *e++ = 50; /* extended rates */ + *e++ = sizeof(more_rates); + memcpy(e, more_rates, sizeof(more_rates)); + e += sizeof(more_rates); + b->elements_length = (uint16_t)(e - b->elements); + + memcpy(vsta_ops.address, address, ETHER_ADDR_LEN); + redwood_sta_attach(&vsta_ops); + /* joined at the start, and again whenever iOS turns Wi-Fi back on (as the phone joins the network of its file) */ + bzero(&join, sizeof(join)); + memcpy(join.ssid, b->ssid, length); + join.ssid_length = b->ssid_length; + join.security = REDWOOD_STA_OPEN; + if (redwood_sta_ask_join_standing(&join) != 0) { + printf("net: the virtual station did not take the standing join\n"); + } + printf("net: en0 is a radio (virtio_net_wifi=1): one open network, \"%s\", joined at the start\n", name); +} + void redwood_virtio_net_init(void) { - uint32_t enabled = 1; + uint32_t enabled = 1, wifi = 0; if (PE_parse_boot_argn("virtio_net", &enabled, sizeof(enabled)) && !enabled) { return; @@ -180,7 +350,15 @@ redwood_virtio_net_init(void) if (vio_find(VIRTIO_ID_NET, 0) == NULL) { return; /* quietly: most machines have none */ } + if (PE_parse_boot_argn("virtio_net_wifi", &wifi, sizeof(wifi)) && wifi) { + virtio_link.wireless = true; + virtio_link.up = false; /* until the station has joined its network */ + } if (!virtio_net_start(virtio_link.address) || !redwood_net_attach(&virtio_link)) { printf("net: no network interface\n"); + return; + } + if (virtio_link.wireless) { + vsta_start(virtio_link.address); } }