Skip to content
Merged
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
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ members = [
"compd",
"init",
"shelld",
"settings",
"tests/syscall-e2e",
"tests/netcheck",
"tests/spinning-cube",
"tests/system-visualizer",
]
Expand Down
9 changes: 9 additions & 0 deletions bootloader/src/baremetal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use core::sync::atomic::{AtomicBool, Ordering};
use morpheus_display::console::TextConsole;

use crate::baremetal_ops::network;

// TYPES THAT CROSS THE BORDER

/// Raw framebuffer info from GOP. stride is pixels_per_scan_line from GOP.
Expand Down Expand Up @@ -287,6 +289,13 @@ pub unsafe fn enter_baremetal(config: BaremetalEntryConfig) -> ! {
}
};

// userspace network activation hook. we stay offline by default.
network::init_userspace_network_activation(morpheus_network::dma::DmaRegion::new(
platform.dma().cpu_base(),
platform.dma().bus_base(),
platform.dma().size(),
), platform.tsc_freq());

// persistent storage — try to mount a real block device
crate::storage::init_persistent_storage(platform.dma(), platform.tsc_freq());

Expand Down
1 change: 1 addition & 0 deletions bootloader/src/baremetal_ops/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod network;
115 changes: 115 additions & 0 deletions bootloader/src/baremetal_ops/network/activate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use morpheus_network::boot::{probe_and_create_driver, ProbeError, ProbeResult};
use morpheus_network::device::UnifiedNetDevice;
use morpheus_network::stack::{NetConfig, NetInterface};

use super::{config, nic, state, tcp, udp_dns};

unsafe fn activate_network_from_userspace() -> i64 {
morpheus_hwinit::serial::log_info("NET", 940, "userspace activation requested");

if state::has_driver() {
morpheus_hwinit::serial::log_info("NET", 941, "already active");
return 1;
}

let Some((dma, tsc_freq)) = state::activation_context() else {
morpheus_hwinit::serial::log_error("NET", 942, "activation failed: dma unavailable");
return -1;
};

morpheus_hwinit::serial::log_info("NET", 943, "probing NIC via network::probe_and_create_driver");
let driver = match probe_and_create_driver(dma, tsc_freq) {
Ok(ProbeResult::VirtIO(v)) => {
morpheus_hwinit::serial::log_info("NET", 949, "probe selected virtio NIC");
UnifiedNetDevice::VirtIO(v)
}
Ok(ProbeResult::Intel(i)) => {
morpheus_hwinit::serial::log_info("NET", 951, "probe selected intel NIC");
UnifiedNetDevice::Intel(i)
}
Err(ProbeError::NoDevice) => {
morpheus_hwinit::serial::log_error("NET", 944, "probe failed: no supported NIC detected");
nic::log_pci_network_candidates();
return -2;
}
Err(ProbeError::VirtioInitFailed) => {
morpheus_hwinit::serial::log_error("NET", 950, "virtio init failed");
return -3;
}
Err(ProbeError::IntelInitFailed) => {
morpheus_hwinit::serial::log_error("NET", 952, "intel init failed");
return -3;
}
Err(ProbeError::DeviceNotResponding) => {
morpheus_hwinit::serial::log_error("NET", 955, "nic mmio not responding");
return -4;
}
Err(ProbeError::BarMappingFailed) => {
morpheus_hwinit::serial::log_error("NET", 956, "nic bar mapping failure");
return -5;
}
};

morpheus_hwinit::serial::log_ok("NET", 945, "driver initialized");

let stack = NetInterface::new(driver, NetConfig::dhcp());
state::clear_net_handle_tables();
state::set_stack(stack);

morpheus_hwinit::serial::log_info("NET", 946, "registering NIC ops");
morpheus_hwinit::register_nic(morpheus_hwinit::NicOps {
tx: Some(nic::user_net_tx),
rx: Some(nic::user_net_rx),
link_up: Some(nic::user_net_link_up),
mac: Some(nic::user_net_mac),
refill: Some(nic::user_net_refill),
ctrl: Some(nic::user_net_ctrl),
});

morpheus_hwinit::serial::log_info("NET", 957, "registering net stack ops");
morpheus_hwinit::register_net_stack(morpheus_hwinit::NetStackOps {
tcp_socket: Some(tcp::net_tcp_socket_impl),
tcp_connect: Some(tcp::net_tcp_connect_impl),
tcp_send: Some(tcp::net_tcp_send_impl),
tcp_recv: Some(tcp::net_tcp_recv_impl),
tcp_close: Some(tcp::net_tcp_close_impl),
tcp_state: Some(tcp::net_tcp_state_impl),
tcp_listen: Some(tcp::net_tcp_listen_impl),
tcp_accept: Some(tcp::net_tcp_accept_impl),
tcp_shutdown: Some(tcp::net_tcp_shutdown_impl),
tcp_nodelay: Some(tcp::net_tcp_nodelay_impl),
tcp_keepalive: Some(tcp::net_tcp_keepalive_impl),
udp_socket: Some(udp_dns::net_udp_socket_impl),
udp_send_to: Some(udp_dns::net_udp_send_to_impl),
udp_recv_from: Some(udp_dns::net_udp_recv_from_impl),
udp_close: Some(udp_dns::net_udp_close_impl),
dns_start: Some(udp_dns::net_dns_start_impl),
dns_result: Some(udp_dns::net_dns_result_impl),
dns_set_servers: Some(udp_dns::net_dns_set_servers_impl),
cfg_get: Some(config::net_cfg_get),
cfg_dhcp: Some(config::net_cfg_dhcp),
cfg_static_ip: Some(config::net_cfg_static_ip),
cfg_hostname: Some(config::net_cfg_hostname),
poll_drive: Some(config::net_poll_drive),
poll_stats: Some(config::net_poll_stats),
});

let _ = nic::user_net_refill();

let link_now = nic::user_net_link_up();
if link_now != 0 {
morpheus_hwinit::serial::log_ok("NET", 947, "activation complete: link up");
} else {
morpheus_hwinit::serial::log_info("NET", 948, "activation complete: link down");
}

0
}

pub(super) unsafe fn init_userspace_network_activation(
dma: morpheus_network::dma::DmaRegion,
tsc_freq: u64,
) {
state::set_activation_context(dma, tsc_freq);
morpheus_hwinit::register_net_activation(activate_network_from_userspace);
}
91 changes: 91 additions & 0 deletions bootloader/src/baremetal_ops/network/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use morpheus_network::stack::NetState;

use super::state;

pub(super) unsafe fn net_cfg_get(buf: *mut u8) -> i64 {
let Some(stack) = state::user_net_stack_mut() else {
return -1;
};

let out = &mut *(buf as *mut morpheus_hwinit::NetConfigInfo);
core::ptr::write_bytes(
out as *mut _ as *mut u8,
0,
core::mem::size_of::<morpheus_hwinit::NetConfigInfo>(),
);

out.state = match stack.state() {
NetState::Unconfigured => 0,
NetState::DhcpDiscovering => 1,
NetState::Ready => 2,
NetState::Error => 3,
};

out.flags |= 1;

if let Some(ip) = stack.ipv4_addr() {
out.ipv4_addr = u32::from_be_bytes(ip.octets());
out.prefix_len = 24;
}
if let Some(gw) = stack.gateway() {
out.gateway = u32::from_be_bytes(gw.octets());
out.flags |= 1 << 1;
}
if let Some(dns) = stack.dns() {
out.dns_primary = u32::from_be_bytes(dns.octets());
out.flags |= 1 << 2;
}

let mac = stack.mac_address();
out.mac[..6].copy_from_slice(&mac);
out.mtu = 1500;
state::write_hostname_to(out);

0
}

pub(super) unsafe fn net_cfg_dhcp() -> i64 {
let Some(stack) = state::user_net_stack_mut() else {
return -1;
};

match stack.restart_dhcp() {
Ok(()) => 0,
Err(_) => -1,
}
}

pub(super) unsafe fn net_cfg_static_ip(_ip: u32, _prefix_len: u8, _gateway: u32) -> i64 {
-1
}

pub(super) unsafe fn net_cfg_hostname(name: *const u8, len: usize) -> i64 {
state::set_hostname(name, len)
}

pub(super) unsafe fn net_poll_drive(timestamp_ms: u64) -> i64 {
let Some(stack) = state::user_net_stack_mut() else {
return -1;
};

stack.device_mut().refill_rx_queue();
let activity = stack.poll(timestamp_ms);
stack.device_mut().collect_tx_completions();
if activity { 1 } else { 0 }
}

pub(super) unsafe fn net_poll_stats(buf: *mut u8) -> i64 {
if state::user_net_stack_mut().is_none() {
return -1;
}
let out = &mut *(buf as *mut morpheus_hwinit::NetStats);
core::ptr::write_bytes(
out as *mut _ as *mut u8,
0,
core::mem::size_of::<morpheus_hwinit::NetStats>(),
);
out.tx_packets = morpheus_network::stack::tx_packet_count() as u64;
out.rx_packets = morpheus_network::stack::rx_packet_count() as u64;
out.tcp_active = state::tcp_active_count();
0
}
13 changes: 13 additions & 0 deletions bootloader/src/baremetal_ops/network/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
mod activate;
mod config;
mod nic;
mod state;
mod tcp;
mod udp_dns;

pub unsafe fn init_userspace_network_activation(
dma: morpheus_network::dma::DmaRegion,
tsc_freq: u64,
) {
activate::init_userspace_network_activation(dma, tsc_freq);
}
113 changes: 113 additions & 0 deletions bootloader/src/baremetal_ops/network/nic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use morpheus_network::device::NetworkDevice;

use super::state;

pub(super) unsafe fn log_pci_network_candidates() {
let mut found = 0u32;
for bus in 0..=255u8 {
for dev in 0..32u8 {
for func in 0..8u8 {
let addr = morpheus_hwinit::PciAddr::new(bus, dev, func);
let vendor = morpheus_hwinit::pci_cfg_read16(addr, 0x00);
if vendor == 0xFFFF {
if func == 0 {
break;
}
continue;
}

let class = morpheus_hwinit::pci_cfg_read8(addr, 0x0B);
let subclass = morpheus_hwinit::pci_cfg_read8(addr, 0x0A);
if class != 0x02 {
if func == 0 {
let header = morpheus_hwinit::pci_cfg_read8(addr, 0x0E);
if (header & 0x80) == 0 {
break;
}
}
continue;
}

let device = morpheus_hwinit::pci_cfg_read16(addr, 0x02);
found = found.wrapping_add(1);
morpheus_hwinit::serial::puts("[INFO] [NET] pci net candidate bdf=");
morpheus_hwinit::serial::put_hex64(
((bus as u64) << 16) | ((dev as u64) << 8) | func as u64,
);
morpheus_hwinit::serial::puts(" ven=");
morpheus_hwinit::serial::put_hex64(vendor as u64);
morpheus_hwinit::serial::puts(" dev=");
morpheus_hwinit::serial::put_hex64(device as u64);
morpheus_hwinit::serial::puts(" sub=");
morpheus_hwinit::serial::put_hex64(subclass as u64);
morpheus_hwinit::serial::puts("\n");

if func == 0 {
let header = morpheus_hwinit::pci_cfg_read8(addr, 0x0E);
if (header & 0x80) == 0 {
break;
}
}
}
}
}

if found == 0 {
morpheus_hwinit::serial::log_warn("NET", 953, "pci scan found zero class-0x02 devices");
} else {
morpheus_hwinit::serial::log_info("NET", 954, "pci net candidates logged");
}
}

pub(super) unsafe fn user_net_tx(frame: *const u8, len: usize) -> i64 {
let Some(driver) = state::user_net_driver_mut() else {
return -1;
};
let frame = core::slice::from_raw_parts(frame, len);
if driver.transmit(frame).is_ok() {
0
} else {
-1
}
}

pub(super) unsafe fn user_net_rx(buf: *mut u8, buf_len: usize) -> i64 {
let Some(driver) = state::user_net_driver_mut() else {
return -1;
};
let buf = core::slice::from_raw_parts_mut(buf, buf_len);
match driver.receive(buf) {
Ok(Some(n)) => n as i64,
Ok(None) => 0,
Err(_) => -1,
}
}

pub(super) unsafe fn user_net_link_up() -> i64 {
let Some(driver) = state::user_net_driver_mut() else {
return 0;
};
driver.link_up() as i64
}

pub(super) unsafe fn user_net_mac(out: *mut u8) -> i64 {
let Some(driver) = state::user_net_driver_mut() else {
return -1;
};
let mac = driver.mac_address();
core::ptr::copy_nonoverlapping(mac.as_ptr(), out, 6);
0
}

pub(super) unsafe fn user_net_refill() -> i64 {
let Some(driver) = state::user_net_driver_mut() else {
return -1;
};
driver.refill_rx_queue();
driver.collect_tx_completions();
0
}

pub(super) unsafe fn user_net_ctrl(_cmd: u32, _arg: u64) -> i64 {
-1
}
Loading
Loading