Skip to content

Commit

Permalink
build: app package fmt, migrate to just
Browse files Browse the repository at this point in the history
  • Loading branch information
portasynthinca3 committed Jul 30, 2024
1 parent e28332c commit d150f73
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 139 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/target
/build
.build
/.vscode
/.gdbinit
/.gdb_history
Expand Down
86 changes: 0 additions & 86 deletions Makefile

This file was deleted.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ Base image:
## I wanna run it!!!!
I see that I'm not the only one fueled by bad decisions. Anyways, you will need:
- Git
- Just
- MTools
- Rust v1.82-nightly
- Make
- Erlang/OTP 27
- QEMU
- OVMF (or other UEFI firmware for QEMU)
Expand Down
5 changes: 5 additions & 0 deletions apps/app.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{application, APP,
[{description, "My glorious APP app"},
{vsn, "0.1"},
{modules, [main]},
{export_modules, [main]}]}.
3 changes: 2 additions & 1 deletion base/base.app.src → apps/base/src/base.app.src
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{application, base,
[{description, "Modules needed to bootstrap a functional BOSS system"},
{vsn, "0.1"},
{modules, [gen_port, main]}]}.
{modules, [gen_port, main]},
{export_modules, [gen_port]}]}.
40 changes: 24 additions & 16 deletions base/gen_port.erl → apps/base/src/gen_port.erl
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
%%% Abstraction for talking to ports. The corresponding Rust abstraction is
%%% implemented in `src/vm/port.rs`.

-module(gen_port).
-export([call/2, call/3, call/4]).

%% Performs a synchronous call to a port with a timeout of 5 seconds and with no
%% access token. For a more detailed description, see `call/4`.
-moduledoc "
Abstraction for talking to ports. The corresponding Rust abstraction is
implemented in `src/vm/port.rs`.
".

-doc "
Performs a synchronous call to a port with a timeout of 5 seconds and with no
access token. For a more detailed description, see `call/4`.
".
-spec call(Port :: port(), Request :: term()) -> {error, timeout} | Response :: term().
call(Port, Request) -> call(Port, Request, notoken, 5000).

%% Performs a synchronous call to a port with a timeout of 5 seconds. For a more
%% detailed description, see `call/4`.
-spec call(Port :: port(), Request :: term(), Token :: reference()) -> {error, timeout} | Response :: term().
-doc "
Performs a synchronous call to a port with a timeout of 5 seconds. For a more
detailed description, see `call/4`.
".
-spec call(Port :: port(), Request :: term(), Token :: reference() | notoken) -> {error, timeout} | Response :: term().
call(Port, Request, Token) -> call(Port, Request, Token, 5000).

%% Performs a synchronous call to a port.
%%
%% Port calls usually require an access token that must be obtained somehow.
%% Usually, it will be passed down from a parent in the supervision tree. If an
%% access token is not required, `notoken` may be passed to `Token`.
%%
%% If the port does not respond in `Timeout` milliseconds, `{error, timeout}` is
%% returned.
-doc "
Performs a synchronous call to a port.
Port calls usually require an access token that must be obtained somehow.
Usually, it will be passed down from a parent in the supervision tree. If an
access token is not required, `notoken` may be passed to `Token`.
If the port does not respond in `Timeout` milliseconds, `{error, timeout}` is
returned.
".
-spec call(Port :: port(), Request :: term(), Token :: reference() | notoken, Timeout :: integer()) ->
{error, timeout} | Response :: term().
call(Port, Request, Token, Timeout)
Expand Down
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions apps/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
erlc_flags := "-b beam"

clean APP:
rm -rf .build/{{APP}}

build APP:
mkdir -p .build/{{APP}}/ebin
erlc {{erlc_flags}} -o .build/{{APP}}/ebin {{APP}}/src/*.erl
./etfify {{APP}}/src/{{APP}}.app.src .build/{{APP}}/ebin/app
cp -r {{APP}}/resources .build/{{APP}}/
tar cf .build/{{APP}}/{{APP}}.bop -C .build/{{APP}} ebin/

create APP:
mkdir -p {{APP}}/{resources,src}
cat app.src | sed -e "s/APP/{{APP}}/g" > {{APP}}/src/{{APP}}.app.src
1 change: 0 additions & 1 deletion base/emu.cfg

This file was deleted.

59 changes: 59 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
clean:
rm -rf target/x86_64-boss-uefi
rm -rf .build
rm -rf apps/.build
mkdir .build

# Base image
bosbaima:
mkdir -p .build/bosbaima
date > .build/bosbaima/date
just apps/ build base
cp apps/.build/base/base.bop .build/bosbaima/
tar cf .build/BOSBAIMA.TAR -C .build/bosbaima base.bop date

profile := "release" # "dev" or "release"
profile_dir := "release" # "debug" or "release"
features := ","
cargo_flags := "--target x86_64-boss-uefi.json -Zbuild-std=core,compiler_builtins,alloc -Zbuild-std-features=compiler-builtins-mem --profile " + profile + " --features " + features
magic_section_offset := "0x141000000"
reloc_section_offset := "0x141001000"
# EFI executable
emulator:
cargo build {{cargo_flags}}
cargo clippy {{cargo_flags}}
# magic! (read mem_manager::reloc::relocate_pe for an explanation)
dd if=/dev/random of=.build/rand bs=1 count=1024 2> /dev/null
dd if=/dev/zero of=.build/zero bs=1 count=14 2> /dev/null
cat .build/rand .build/rand > .build/reloc-magic
objdump -hj.data target/x86_64-boss-uefi/{{profile_dir}}/boss.efi | tail -n+6 | head -n1 >> .build/reloc-magic
cat .build/zero >> .build/reloc-magic
objcopy target/x86_64-boss-uefi/{{profile_dir}}/boss.efi \
--add-section .reloc-magic=.build/reloc-magic \
--change-section-address .reloc-magic={{magic_section_offset}} \
--change-section-address .reloc={{reloc_section_offset}} \
.build/BOOTX64.EFI

image_size := "65536" # sectors
image_size_mb := "32"
# Bootable image
iso: emulator bosbaima
dd if=/dev/zero of=.build/boss.iso bs=1M count={{image_size_mb}} 2> /dev/null
mformat -i .build/boss.iso -T {{image_size}}
mmd -i .build/boss.iso ::/EFI
mmd -i .build/boss.iso ::/EFI/BOOT
mmd -i .build/boss.iso ::/BOSS
mcopy -i .build/boss.iso .build/BOOTX64.EFI ::/EFI/BOOT
mcopy -i .build/boss.iso .build/BOSBAIMA.TAR ::/BOSS/BOSBAIMA.TAR

# Boot
qemu: iso
qemu-system-x86_64 -enable-kvm \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/ovmf/x64/OVMF.fd \
-device ahci,id=ahci \
-device ide-hd,drive=disk,bus=ahci.0 \
-drive if=none,id=disk,format=raw,file=.build/boss.iso \
-m 128 \
-smp 1,sockets=1,cores=1,threads=1 \
-boot menu=off,splash-time=0 \
-serial stdio
36 changes: 33 additions & 3 deletions src/vm/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@
//! isolation of applications. This behavior is unwarranted and unexpected in
//! traditional OTP code, and thus a compatibility mode is needed.
use alloc::{rc::Rc, boxed::Box};
use alloc::{boxed::Box, format, rc::Rc};

use hashbrown::HashMap;

use super::{module::Module, state::{LocalAtomRef, LocalContext}, term::{LocalTerm, TermError}};
use super::{module::{self, Module}, state::{LocalAtomRef, LocalContext}, term::{LocalTerm, TermError}};
use crate::util::tar::TarFile;

/// Application package load error
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum LoadError {
/// No app metadata file in app package
NoSpec,
/// Invalid app metadata file in app package
BadSpec(TermError),
/// No module in app package
NoModule(LocalAtomRef),
/// Invalid module data in app package
BadModule(LocalAtomRef, module::LoadError),
}

/// Named collection of related modules
///
Expand All @@ -27,7 +41,7 @@ pub struct Application {

impl Application {
/// Parses a binary `.app` specification
pub fn new(data: &[u8], context: &mut LocalContext) -> Result<Application, TermError> {
pub fn from_app_file(data: &[u8], context: &mut LocalContext) -> Result<Application, TermError> {
// parse ETF
let term = match LocalTerm::from_etf(data, context) {
Ok(term) => term,
Expand All @@ -48,4 +62,20 @@ impl Application {

Ok(Application { name, version, modules })
}

/// Parses a BOSS package (`.bop`) application file
pub fn from_bop_file(data: &[u8], context: &mut LocalContext) -> Result<Application, LoadError> {
let package = TarFile::new(data);
let spec = package.read_file("ebin/app").ok_or(LoadError::NoSpec)?;
let mut app = Self::from_app_file(spec, context).map_err(LoadError::BadSpec)?;

for (name, module) in app.modules.iter_mut() {
let name_str = name.get_str();
let path = format!("ebin/{name_str}.beam");
let data = package.read_file(path.as_str()).ok_or(LoadError::NoModule(name.clone()))?;
*module = Some(Module::new(data, context).map_err(|e| LoadError::BadModule(name.clone(), e))?.into());
}

Ok(app)
}
}
41 changes: 12 additions & 29 deletions src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@
//! implement. Everything else around this module and its descendants is more or
//! less just a generic microkernel.
use alloc::{format, rc::Rc};

use hashbrown::HashMap;

use crate::util::tar::TarFile;
use app::Application;
use app::{Application, LoadError};
use port::LogPort;
use interpreter::{BeamInterpreter, BeamInterpreterMakeError};
use module::{Module, LoadError};
use scheduler::{PrimitiveScheduler, Schedule};
use state::LocalContext;
use term::{LocalTerm, MapTerm, TermError};
use term::{LocalTerm, MapTerm};

pub const CURRENT_OPCODE_MAX: usize = 178;

Expand All @@ -27,27 +24,18 @@ pub mod state;
pub mod port;

/// Virtual machine initialization error
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InitError {
/// No `ebin/base.app` file in base image
/// No base application package in base image
NoBaseApp,
/// No `ebin/{module}.beam` file as requested by the app descriptor
NoBaseModule,
/// Bad data in `ebin/base.app`
BadSpec(TermError),
/// Bad data in `ebin/{module}.beam`
BadModule(LoadError),
/// Base application load error
BaseApp(LoadError),
/// Interpreter initialization error
Interpreter(BeamInterpreterMakeError)
}
impl From<TermError> for InitError {
fn from(value: TermError) -> Self {
Self::BadSpec(value)
}
Interpreter(BeamInterpreterMakeError),
}
impl From<LoadError> for InitError {
fn from(value: LoadError) -> Self {
Self::BadModule(value)
Self::BaseApp(value)
}
}
impl From<BeamInterpreterMakeError> for InitError {
Expand All @@ -66,15 +54,10 @@ pub fn init(base_image: &TarFile) -> Result<!, InitError> {
let x86_64_uefi_atom = context.atom_table.get_or_make_atom("x86_64-uefi");
let main_atom = context.atom_table.get_or_make_atom("main");

// load base application and modules
let base_app = base_image.read_file("ebin/base.app").ok_or(InitError::NoBaseApp)?;
let mut base_app = Application::new(base_app, &mut context)?;
for (name, module) in base_app.modules.iter_mut() {
let path = format!("ebin/{name}.beam");
let module_data = base_image.read_file(path.as_str()).ok_or(InitError::NoBaseModule)?;
*module = Some(Rc::new(Module::new(module_data, &mut context)?));
}
context.applications.insert(base_atom.clone(), base_app);
// load base application package
let base_app = base_image.read_file("base.bop").ok_or(InitError::NoBaseApp)?;
let base_app = Application::from_bop_file(base_app, &mut context)?;
context.applications.insert(base_app.name.clone(), base_app);

// create a scheduler and initial ports
let mut scheduler = PrimitiveScheduler::new(context, 0);
Expand Down
2 changes: 1 addition & 1 deletion src/vm/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::{app::Application, scheduler::LocalTransferAgent, term::LocalTerm};
pub struct LocalAtomRef(usize, Weak<str>);

impl LocalAtomRef {
fn get_str(&self) -> Rc<str> {
pub fn get_str(&self) -> Rc<str> {
self.1.upgrade().expect("inconsistent atom table state: string freed before weak reference was dropped")
}
}
Expand Down

0 comments on commit d150f73

Please sign in to comment.