Skip to content
Closed
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
Binary file added .keys/app_store_publisher_ed25519.pub
Binary file not shown.
Binary file added .keys/app_store_publisher_mldsa65.pub
Binary file not shown.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ nonos-capsule-image-codec = []
nonos-capsule-image-viewer = []
nonos-capsule-video-player = []
nonos-capsule-about = []
nonos-capsule-app-store = []
nonos-capsule-audio-player = []
nonos-capsule-hello = []
nonos-capsule-boot-splash = []
Expand Down Expand Up @@ -552,6 +553,7 @@ microkernel-desktop-base = [
"nonos-capsule-wallpaper-catalog",
"nonos-capsule-toolkit",
"nonos-capsule-about",
"nonos-capsule-app-store",
"nonos-capsule-linux",
"nonos-capsule-audio",
"nonos-capsule-driver-hda",
Expand Down
1 change: 1 addition & 0 deletions mk/20-build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ include userland/capsule_clipboard/Capsule.mk
include userland/capsule_login/Capsule.mk
include userland/toolkit/Capsule.mk
include userland/capsule_about/Capsule.mk
include userland/capsule_app_store/Capsule.mk
include userland/capsule_linux/Capsule.mk
include userland/capsule_hello/Capsule.mk
include userland/capsule_gui_demo/Capsule.mk
Expand Down
60 changes: 60 additions & 0 deletions src/kernel_core/surface_registry/share/attach_frames.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Giving a receiver its own view of a surface's frames.

use crate::kernel_core::surface_registry::table::SLOTS;
use crate::kernel_core::surface_registry::types::{
decode_handle, RegistryError, SurfaceDescriptor, SurfaceHandle,
};
use crate::memory::paging::manager::api::{lookup_asid_for_process, map_page_in_asid};
use crate::memory::paging::types::PagePermissions;
use crate::process::current_process;

pub(super) fn attach_frames(
receiver_pid: u32,
handle: SurfaceHandle,
out_desc: &mut SurfaceDescriptor,
) -> Result<u64, RegistryError> {
let (idx, epoch) = decode_handle(handle);
let frames = {
let mut slots = SLOTS.lock();
let slot =
slots.get_mut(idx as usize).and_then(|s| s.as_mut()).ok_or(RegistryError::BadHandle)?;
if slot.epoch != epoch {
#[cfg(feature = "dbg-ring")]
crate::log::dbg_ring::dbg_emit_2u64(0x5546_0001, handle, slot.epoch as u64);
return Err(RegistryError::BadHandle);
}
slot.refcount = slot.refcount.checked_add(1).ok_or(RegistryError::InvalidArg)?;
slot.frames.clone()
};
let mut desc = super::descriptor::descriptor(handle)?;
let asid = lookup_asid_for_process(receiver_pid).ok_or(RegistryError::MapFailed)?;
let proc = current_process().ok_or(RegistryError::NoProc)?;
let base = proc
.reserve_vma(frames.len().saturating_mul(4096))
.map_err(|_| RegistryError::MapFailed)?;
let perms = PagePermissions::user_rw();
for (i, frame) in frames.iter().enumerate() {
let va = crate::memory::addr::VirtAddr::new(base.as_u64() + (i as u64) * 4096);
map_page_in_asid(asid, va, *frame, perms).map_err(|_| RegistryError::MapFailed)?;
}
desc.base_va = base.as_u64();
*out_desc = desc;
super::super::attach_map::record(receiver_pid, handle, base.as_u64(), out_desc.byte_len);
Ok(base.as_u64())
}
70 changes: 13 additions & 57 deletions src/kernel_core/surface_registry/share/attach_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,32 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::kernel_core::surface_registry::table::SLOTS;
//! Handing a surface to another process.

use crate::kernel_core::surface_registry::types::{
decode_handle, RegistryError, SurfaceDescriptor, SurfaceHandle,
RegistryError, SurfaceDescriptor, SurfaceHandle,
};
use crate::memory::paging::manager::api::{lookup_asid_for_process, map_page_in_asid};
use crate::memory::paging::types::PagePermissions;
use crate::process::current_process;

use super::attach_frames::attach_frames;
use super::self_attach::self_attach;

pub fn attach_surface(
receiver_pid: u32,
handle: SurfaceHandle,
out_desc: &mut SurfaceDescriptor,
) -> Result<u64, RegistryError> {
// Never to a guest.
if crate::process::foreign::is_foreign(receiver_pid) {
return Err(RegistryError::InvalidArg);
}
if let Some((base_va, byte_len)) = super::super::attach_map::lookup(receiver_pid, handle) {
*out_desc = super::descriptor::descriptor(handle)?;
out_desc.base_va = base_va;
out_desc.byte_len = byte_len;
return Ok(base_va);
}
let (idx, epoch) = decode_handle(handle);
// A self-attach (the owner attaching its own surface) needs no new
// mapping: the surface already lives at the VA the owner registered
// it at. Returning that VA keeps the owner's existing VMA, which the
// present path resolves against. Remapping would create a second VA
// with no backing VMA and break MkSurfacePresent.
{
let slots = SLOTS.lock();
let slot =
slots.get(idx as usize).and_then(|s| s.as_ref()).ok_or(RegistryError::BadHandle)?;
if slot.epoch != epoch {
#[cfg(feature = "dbg-ring")]
crate::log::dbg_ring::dbg_emit_2u64(0x5546_0001, handle, slot.epoch as u64);
return Err(RegistryError::BadHandle);
}
if slot.owner_pid == receiver_pid && slot.owner_base_va != 0 {
let base_va = slot.owner_base_va;
let byte_len = slot.byte_len;
drop(slots);
*out_desc = super::descriptor::descriptor(handle)?;
out_desc.base_va = base_va;
out_desc.byte_len = byte_len;
super::super::attach_map::record(receiver_pid, handle, base_va, byte_len);
return Ok(base_va);
}
}
let frames = {
let mut slots = SLOTS.lock();
let slot =
slots.get_mut(idx as usize).and_then(|s| s.as_mut()).ok_or(RegistryError::BadHandle)?;
if slot.epoch != epoch {
#[cfg(feature = "dbg-ring")]
crate::log::dbg_ring::dbg_emit_2u64(0x5546_0001, handle, slot.epoch as u64);
return Err(RegistryError::BadHandle);
}
slot.refcount = slot.refcount.checked_add(1).ok_or(RegistryError::InvalidArg)?;
slot.frames.clone()
};
let mut desc = super::descriptor::descriptor(handle)?;
let asid = lookup_asid_for_process(receiver_pid).ok_or(RegistryError::MapFailed)?;
let proc = current_process().ok_or(RegistryError::NoProc)?;
let base = proc
.reserve_vma(frames.len().saturating_mul(4096))
.map_err(|_| RegistryError::MapFailed)?;
let perms = PagePermissions::user_rw();
for (i, frame) in frames.iter().enumerate() {
let va = crate::memory::addr::VirtAddr::new(base.as_u64() + (i as u64) * 4096);
map_page_in_asid(asid, va, *frame, perms).map_err(|_| RegistryError::MapFailed)?;
if let Some(base_va) = self_attach(receiver_pid, handle, out_desc)? {
return Ok(base_va);
}
desc.base_va = base.as_u64();
*out_desc = desc;
super::super::attach_map::record(receiver_pid, handle, base.as_u64(), out_desc.byte_len);
Ok(base.as_u64())
attach_frames(receiver_pid, handle, out_desc)
}
2 changes: 2 additions & 0 deletions src/kernel_core/surface_registry/share/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

mod attach_frames;
mod attach_surface;
mod descriptor;
mod self_attach;
mod share_surface;

pub use attach_surface::attach_surface;
Expand Down
49 changes: 49 additions & 0 deletions src/kernel_core/surface_registry/share/self_attach.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! The owner attaching its own surface.

use crate::kernel_core::surface_registry::table::SLOTS;
use crate::kernel_core::surface_registry::types::{
decode_handle, RegistryError, SurfaceDescriptor, SurfaceHandle,
};

/// The owner's own va when the owner is the receiver, or `None` when the
/// receiver is somebody else and a real mapping has to be made.
pub(super) fn self_attach(
receiver_pid: u32,
handle: SurfaceHandle,
out_desc: &mut SurfaceDescriptor,
) -> Result<Option<u64>, RegistryError> {
let (idx, epoch) = decode_handle(handle);
let slots = SLOTS.lock();
let slot = slots.get(idx as usize).and_then(|s| s.as_ref()).ok_or(RegistryError::BadHandle)?;
if slot.epoch != epoch {
#[cfg(feature = "dbg-ring")]
crate::log::dbg_ring::dbg_emit_2u64(0x5546_0001, handle, slot.epoch as u64);
return Err(RegistryError::BadHandle);
}
if slot.owner_pid != receiver_pid || slot.owner_base_va == 0 {
return Ok(None);
}
let (base_va, byte_len) = (slot.owner_base_va, slot.byte_len);
drop(slots);
*out_desc = super::descriptor::descriptor(handle)?;
out_desc.base_va = base_va;
out_desc.byte_len = byte_len;
super::super::attach_map::record(receiver_pid, handle, base_va, byte_len);
Ok(Some(base_va))
}
49 changes: 49 additions & 0 deletions src/userspace/capsule_app_store/embed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

// Build-time embed of the marketplace window.

#[cfg(feature = "nonos-capsule-app-store")]
pub(crate) const APP_STORE_ELF: &[u8] =
include_bytes!(concat!(
"../../../userland/capsule_app_store/target/",
env!("NONOS_USER_TARGET"),
"/release/app_store"
));

#[cfg(feature = "nonos-capsule-app-store")]
pub(crate) const APP_STORE_NONOS_ID_CERT_BYTES: &[u8] =
include_bytes!("../../../nonos-data/trust/capsules/app_store.nonos_id_cert.bin");

#[cfg(feature = "nonos-capsule-app-store")]
pub(crate) const APP_STORE_MANIFEST_BYTES: &[u8] =
include_bytes!("../../../nonos-data/trust/capsules/app_store.manifest.bin");

#[cfg(feature = "nonos-capsule-app-store")]
pub(crate) const APP_STORE_ATTESTATION_BYTES: &[u8] =
include_bytes!("../../../nonos-data/trust/capsules/app_store.zk_trailer.bin");

#[cfg(not(feature = "nonos-capsule-app-store"))]
pub(crate) const APP_STORE_ELF: &[u8] = &[];

#[cfg(not(feature = "nonos-capsule-app-store"))]
pub(crate) const APP_STORE_NONOS_ID_CERT_BYTES: &[u8] = &[];

#[cfg(not(feature = "nonos-capsule-app-store"))]
pub(crate) const APP_STORE_MANIFEST_BYTES: &[u8] = &[];

#[cfg(not(feature = "nonos-capsule-app-store"))]
pub(crate) const APP_STORE_ATTESTATION_BYTES: &[u8] = &[];
24 changes: 24 additions & 0 deletions src/userspace/capsule_app_store/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! The marketplace window, as the kernel spawns it.

mod embed;
mod spawn;
mod state;

pub use spawn::spawn_app_store_capsule;
pub use state::shared_state;
62 changes: 62 additions & 0 deletions src/userspace/capsule_app_store/spawn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use super::embed::{
APP_STORE_ATTESTATION_BYTES, APP_STORE_ELF, APP_STORE_MANIFEST_BYTES,
APP_STORE_NONOS_ID_CERT_BYTES,
};
use super::state;
use crate::capabilities::Capability;
use crate::kernel_core::process_spawn::capsule_spawn::{
self, CapsuleSpecVerified, SpawnError,
};
use crate::security::nonos_id_cert::IdCertVerifyError;
use crate::security::nonos_trust_anchor::{
decode as decode_trust_anchor, BAKED_TRUST_ANCHOR_POLICY,
};

const SERVICE_NAME: &str = "app.store";
const SERVICE_PORT: u32 = 4940;
const REPLY_INBOX: &str = "endpoint.app.store.reply";
const REPLY_PORT: u32 = 4941;
const TARGET_TRIPLE: &str = env!("NONOS_USER_TARGET");

pub fn spawn_app_store_capsule() -> Result<(), SpawnError> {
let trust_anchor = decode_trust_anchor(BAKED_TRUST_ANCHOR_POLICY)
.map_err(|_| SpawnError::NonosIdCertRejected(IdCertVerifyError::TrustAnchorPolicy))?;
let spec = CapsuleSpecVerified {
name: SERVICE_NAME,
service_port: SERVICE_PORT,
reply_inbox: REPLY_INBOX,
reply_port: REPLY_PORT,
elf: APP_STORE_ELF,
nonos_id_cert_bytes: APP_STORE_NONOS_ID_CERT_BYTES,
manifest_bytes: APP_STORE_MANIFEST_BYTES,
attestation_trailer: APP_STORE_ATTESTATION_BYTES,
target_triple: TARGET_TRIPLE,
// It reads one service, paints, and may ask for an install.
requested_caps: Capability::CoreExec.bit()
| Capability::IPC.bit()
| Capability::Memory.bit()
| Capability::GraphicsDisplayQuery.bit()
| Capability::GraphicsSurfaceCreate.bit()
| Capability::AppInstall.bit(),
debug_tag: b"",
};
let pid = capsule_spawn::spawn_verified(&spec, &trust_anchor, None)?;
state::set_alive(pid);
Ok(())
}
Loading
Loading