Compare commits
11 Commits
fe44804dd9
...
41bf78cf98
Author | SHA1 | Date |
---|---|---|
|
41bf78cf98 | |
|
9f0e87b51d | |
|
a93aa3a426 | |
|
2efa13ab43 | |
|
28d9e37b87 | |
|
76fd3fc176 | |
|
2b8ae027df | |
|
4c2492e985 | |
|
da3901e104 | |
|
2df1f6c006 | |
|
afdb024c36 |
|
@ -1,9 +1,11 @@
|
|||
set(glacier_files
|
||||
status/error.cpp
|
||||
string/string.cpp
|
||||
string/string_builder.cpp
|
||||
string/string_view.cpp
|
||||
string/str_format.cpp
|
||||
string/str_split.cpp
|
||||
util/hash.cpp
|
||||
)
|
||||
|
||||
add_library(glacier STATIC
|
||||
|
|
|
@ -7,35 +7,11 @@
|
|||
#include "glacier/container/pair.h"
|
||||
#include "glacier/status/error.h"
|
||||
#include "glacier/string/string.h"
|
||||
#include "glacier/util/hash.h"
|
||||
|
||||
namespace glcr {
|
||||
|
||||
template <typename T>
|
||||
struct HashFunc {
|
||||
uint64_t operator()(const T&);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashFunc<uint64_t> {
|
||||
uint64_t operator()(const uint64_t& value) {
|
||||
// FIXME: Write a real hash function.
|
||||
return 0xABBAABBAABBAABBA ^ value;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct HashFunc<String> {
|
||||
uint64_t operator()(const String& value) {
|
||||
// FIXME: Write a real hash function.
|
||||
uint64_t acc = 0;
|
||||
for (uint64_t i = 0; i < value.length(); i++) {
|
||||
acc += value[i];
|
||||
}
|
||||
return 0xABBAABBAABBAABBA ^ acc;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V, class H = HashFunc<K>>
|
||||
template <typename K, typename V, class H = Hash<K>>
|
||||
class HashMap {
|
||||
public:
|
||||
HashMap() = default;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include "glacier/status/error.h"
|
||||
|
||||
namespace glcr {
|
||||
|
||||
StringView ErrorCodeToStr(ErrorCode code) {
|
||||
switch (code) {
|
||||
case OK:
|
||||
return "OK";
|
||||
case INVALID_ARGUMENT:
|
||||
return "INVALID_ARGUMENT";
|
||||
case NOT_FOUND:
|
||||
return "NOT_FOUND";
|
||||
case PERMISSION_DENIED:
|
||||
return "PERMISSION_DENIED";
|
||||
case NULL_PTR:
|
||||
return "NULL_PTR";
|
||||
case EMPTY:
|
||||
return "EMPTY";
|
||||
case ALREADY_EXISTS:
|
||||
return "ALREADY_EXISTS";
|
||||
case BUFFER_SIZE:
|
||||
return "BUFFER_SIZE";
|
||||
case FAILED_PRECONDITION:
|
||||
return "FAILED_PRECONDITION";
|
||||
case INTERNAL:
|
||||
return "INTERNAL";
|
||||
case UNIMPLEMENTED:
|
||||
return "UNIMPLEMENTED";
|
||||
case EXHAUSTED:
|
||||
return "EXHAUSTED";
|
||||
case INVALID_RESPONSE:
|
||||
return "INVALID_RESPONSE";
|
||||
case CAP_NOT_FOUND:
|
||||
return "CAP_NOT_FOUND";
|
||||
case CAP_WRONG_TYPE:
|
||||
return "CAP_WRONG_TYPE";
|
||||
case CAP_PERMISSION_DENIED:
|
||||
return "CAP_PERMISSION_DENIED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace glcr
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "glacier/string/string_view.h"
|
||||
|
||||
namespace glcr {
|
||||
|
||||
enum ErrorCode : uint64_t {
|
||||
|
@ -29,6 +31,8 @@ enum ErrorCode : uint64_t {
|
|||
|
||||
};
|
||||
|
||||
StringView ErrorCodeToStr(ErrorCode code);
|
||||
|
||||
#define RET_ERR(expr) \
|
||||
{ \
|
||||
glcr::ErrorCode _tmp_err = static_cast<glcr::ErrorCode>(expr); \
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "status/error.h"
|
||||
#include "string/string.h"
|
||||
|
||||
namespace glcr {
|
||||
|
||||
class Status {
|
||||
public:
|
||||
static Status Ok() { return {}; }
|
||||
Status(ErrorCode code) : code_(code), message_() {}
|
||||
Status(ErrorCode code, StringView message) : code_(code), message_(message) {}
|
||||
|
||||
explicit operator bool() { return ok(); }
|
||||
bool ok() { return code_ == OK; }
|
||||
|
||||
ErrorCode code() { return code_; }
|
||||
StringView message() { return message_; }
|
||||
|
||||
private:
|
||||
ErrorCode code_;
|
||||
String message_;
|
||||
|
||||
Status();
|
||||
};
|
||||
|
||||
Status InvalidArgument(StringView message) {
|
||||
return Status(INVALID_ARGUMENT, message);
|
||||
}
|
||||
|
||||
Status NotFound(StringView message) { return Status(NOT_FOUND, message); }
|
||||
|
||||
Status PermissionDenied(StringView message) {
|
||||
return Status(PERMISSION_DENIED, message);
|
||||
}
|
||||
|
||||
Status NullPtr(StringView message) { return Status(NULL_PTR, message); }
|
||||
|
||||
Status Empty(StringView message) { return Status(EMPTY, message); }
|
||||
|
||||
Status AlreadyExists(StringView message) {
|
||||
return Status(ALREADY_EXISTS, message);
|
||||
}
|
||||
|
||||
Status BufferSize(StringView message) { return Status(BUFFER_SIZE, message); }
|
||||
|
||||
Status FailedPrecondition(StringView message) {
|
||||
return Status(FAILED_PRECONDITION, message);
|
||||
}
|
||||
|
||||
Status Internal(StringView message) { return Status(INTERNAL, message); }
|
||||
|
||||
Status Unimplemented(StringView message) {
|
||||
return Status(UNIMPLEMENTED, message);
|
||||
}
|
||||
|
||||
Status Exhausted(StringView message) { return Status(EXHAUSTED, message); }
|
||||
|
||||
Status InvalidResponse(StringView message) {
|
||||
return Status(INVALID_RESPONSE, message);
|
||||
}
|
||||
|
||||
} // namespace glcr
|
|
@ -57,7 +57,7 @@ void StrFormatValue(StringBuilder& builder, const uint64_t& value,
|
|||
template <>
|
||||
void StrFormatValue(StringBuilder& builder, const ErrorCode& value,
|
||||
StringView opts) {
|
||||
StrFormatValue(builder, static_cast<uint64_t>(value), opts);
|
||||
StrFormatValue(builder, ErrorCodeToStr(value), opts);
|
||||
}
|
||||
|
||||
template <>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#include "util/hash.h"
|
||||
|
||||
#include "string/string.h"
|
||||
|
||||
namespace glcr {
|
||||
namespace {
|
||||
|
||||
const uint64_t kFnvOffset = 0xcbf29ce484222325;
|
||||
const uint64_t kFnvPrime = 0x100000001b3;
|
||||
|
||||
} // namespace
|
||||
|
||||
uint64_t Hash<uint64_t>::operator()(const uint64_t& value) {
|
||||
uint64_t hash = kFnvOffset;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
hash *= kFnvPrime;
|
||||
hash ^= (value >> (8 * i)) & 0xFF;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
uint64_t Hash<String>::operator()(const String& value) {
|
||||
uint64_t hash = kFnvOffset;
|
||||
for (uint8_t i = 0; i < value.length(); i++) {
|
||||
hash *= kFnvPrime;
|
||||
hash ^= value[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace glcr
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace glcr {
|
||||
|
||||
// General purpose templated hash function.
|
||||
// Currently the template speciializations
|
||||
// implement FNV hashing:
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||
template <typename T>
|
||||
struct Hash {
|
||||
uint64_t operator()(const T&);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<uint64_t> {
|
||||
uint64_t operator()(const uint64_t&);
|
||||
};
|
||||
|
||||
class String;
|
||||
template <>
|
||||
struct Hash<String> {
|
||||
uint64_t operator()(const String&);
|
||||
};
|
||||
|
||||
} // namespace glcr
|
|
@ -10,6 +10,7 @@ add_library(mammoth STATIC
|
|||
src/process.cpp
|
||||
src/port_client.cpp
|
||||
src/port_server.cpp
|
||||
src/semaphore.cpp
|
||||
src/thread.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <ztypes.h>
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
Semaphore();
|
||||
~Semaphore();
|
||||
|
||||
void Wait();
|
||||
void Signal();
|
||||
|
||||
private:
|
||||
z_cap_t semaphore_cap_;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
#include "mammoth/semaphore.h"
|
||||
|
||||
#include <zcall.h>
|
||||
|
||||
#include "mammoth/debug.h"
|
||||
|
||||
Semaphore::Semaphore() { check(ZSemaphoreCreate(&semaphore_cap_)); }
|
||||
Semaphore::~Semaphore() { check(ZCapRelease(semaphore_cap_)); }
|
||||
|
||||
void Semaphore::Wait() { check(ZSemaphoreWait(semaphore_cap_)); }
|
||||
void Semaphore::Signal() { check(ZSemaphoreSignal(semaphore_cap_)); }
|
|
@ -2,16 +2,11 @@
|
|||
|
||||
## Yellowstone
|
||||
|
||||
- Store registered services in a hashmap.
|
||||
- Requires: Adding hashmap to Glacier.
|
||||
- Start the next service from a configuration file.
|
||||
|
||||
## Denali
|
||||
|
||||
- Migrate denali to a yunq definition.
|
||||
- Requires: Adding async yunq servers (or more synchronization primitives)
|
||||
- Add the ability to send multiple Ahci commands at once.
|
||||
|
||||
## VictoriaFalls
|
||||
|
||||
- Add a read file VFS service.
|
||||
|
|
|
@ -85,7 +85,7 @@ void AhciDevice::HandleIrq() {
|
|||
for (uint64_t i = 0; i < 32; i++) {
|
||||
if (commands_finished & (1 << i)) {
|
||||
commands_issued_ &= ~(1 << i);
|
||||
commands_[i]->Callback();
|
||||
commands_[i]->SignalComplete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
Command::~Command() {}
|
||||
|
||||
DmaReadCommand::DmaReadCommand(uint64_t lba, uint64_t sector_cnt,
|
||||
uint64_t paddr, Mutex& callback_mutex)
|
||||
uint64_t paddr)
|
||||
: lba_(lba),
|
||||
sector_cnt_(sector_cnt),
|
||||
paddr_(paddr),
|
||||
callback_mutex_(callback_mutex) {}
|
||||
callback_semaphore_() {}
|
||||
|
||||
DmaReadCommand::~DmaReadCommand() {}
|
||||
|
||||
|
@ -47,4 +47,6 @@ void DmaReadCommand::PopulatePrdt(PhysicalRegionDescriptor* prdt) {
|
|||
prdt[0].region_address = paddr_;
|
||||
prdt[0].byte_count = sector_cnt_ * 512;
|
||||
}
|
||||
void DmaReadCommand::Callback() { callback_mutex_.Release(); }
|
||||
|
||||
void DmaReadCommand::SignalComplete() { callback_semaphore_.Signal(); }
|
||||
void DmaReadCommand::WaitComplete() { callback_semaphore_.Wait(); }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <mammoth/memory_region.h>
|
||||
#include <mammoth/mutex.h>
|
||||
#include <mammoth/response_context.h>
|
||||
#include <mammoth/semaphore.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ahci/ahci.h"
|
||||
|
@ -12,24 +12,27 @@ class Command {
|
|||
virtual ~Command();
|
||||
virtual void PopulateFis(uint8_t* command_fis) = 0;
|
||||
virtual void PopulatePrdt(PhysicalRegionDescriptor* prdt) = 0;
|
||||
virtual void Callback() = 0;
|
||||
virtual void WaitComplete() = 0;
|
||||
virtual void SignalComplete() = 0;
|
||||
};
|
||||
|
||||
class DmaReadCommand : public Command {
|
||||
public:
|
||||
DmaReadCommand(uint64_t lba, uint64_t sector_cnt, uint64_t dest_paddr,
|
||||
Mutex& callback_mutex);
|
||||
DmaReadCommand(uint64_t lba, uint64_t sector_cnt, uint64_t dest_paddr);
|
||||
|
||||
virtual ~DmaReadCommand() override;
|
||||
|
||||
void PopulateFis(uint8_t* command_fis) override;
|
||||
void PopulatePrdt(PhysicalRegionDescriptor* prdt) override;
|
||||
|
||||
void Callback() override;
|
||||
void WaitComplete() override;
|
||||
void SignalComplete() override;
|
||||
|
||||
private:
|
||||
uint64_t lba_;
|
||||
uint64_t sector_cnt_;
|
||||
uint64_t paddr_;
|
||||
Mutex& callback_mutex_;
|
||||
// TODO: Make this owned by the device so that we don't have to create a new
|
||||
// one with the kernel every time a command is issued.
|
||||
Semaphore callback_semaphore_;
|
||||
};
|
||||
|
|
|
@ -15,19 +15,15 @@ glcr::ErrorOr<glcr::UniquePtr<DenaliServer>> DenaliServer::Create(
|
|||
glcr::ErrorCode DenaliServer::HandleRead(const ReadRequest& req,
|
||||
ReadResponse& resp) {
|
||||
ASSIGN_OR_RETURN(AhciDevice * device, driver_.GetDevice(req.device_id()));
|
||||
ASSIGN_OR_RETURN(Mutex mutex, Mutex::Create());
|
||||
RET_ERR(mutex.Lock());
|
||||
|
||||
uint64_t paddr;
|
||||
OwnedMemoryRegion region =
|
||||
OwnedMemoryRegion::ContiguousPhysical(req.size() * 512, &paddr);
|
||||
|
||||
DmaReadCommand command(req.lba(), req.size(), paddr, mutex);
|
||||
DmaReadCommand command(req.lba(), req.size(), paddr);
|
||||
device->IssueCommand(&command);
|
||||
|
||||
// Wait for read operation to complete.
|
||||
RET_ERR(mutex.Lock());
|
||||
RET_ERR(mutex.Release());
|
||||
command.WaitComplete();
|
||||
|
||||
resp.set_device_id(req.device_id());
|
||||
resp.set_size(req.size());
|
||||
|
@ -38,33 +34,31 @@ glcr::ErrorCode DenaliServer::HandleRead(const ReadRequest& req,
|
|||
glcr::ErrorCode DenaliServer::HandleReadMany(const ReadManyRequest& req,
|
||||
ReadResponse& resp) {
|
||||
ASSIGN_OR_RETURN(AhciDevice * device, driver_.GetDevice(req.device_id()));
|
||||
ASSIGN_OR_RETURN(Mutex mutex, Mutex::Create());
|
||||
RET_ERR(mutex.Lock());
|
||||
|
||||
uint64_t region_paddr;
|
||||
OwnedMemoryRegion region = OwnedMemoryRegion::ContiguousPhysical(
|
||||
req.lba().size() * 512, ®ion_paddr);
|
||||
|
||||
auto& vec = req.lba();
|
||||
uint64_t curr_run_start = 0;
|
||||
for (uint64_t i = 0; i < vec.size(); i++) {
|
||||
if (i + 1 < vec.size() && vec.at(i) + 1 == vec.at(i + 1)) {
|
||||
continue;
|
||||
if (req.lba().size() != req.sector_cnt().size()) {
|
||||
return glcr::INVALID_ARGUMENT;
|
||||
}
|
||||
uint64_t lba = vec.at(curr_run_start);
|
||||
uint64_t size = (i - curr_run_start) + 1;
|
||||
uint64_t paddr = region_paddr + curr_run_start * 512;
|
||||
DmaReadCommand command(lba, size, paddr, mutex);
|
||||
|
||||
uint64_t sector_cnt = 0;
|
||||
for (uint64_t i = 0; i < req.sector_cnt().size(); i++) {
|
||||
sector_cnt += req.sector_cnt().at(i);
|
||||
}
|
||||
uint64_t region_paddr;
|
||||
OwnedMemoryRegion region =
|
||||
OwnedMemoryRegion::ContiguousPhysical(sector_cnt * 512, ®ion_paddr);
|
||||
|
||||
for (uint64_t i = 0; i < req.lba().size(); i++) {
|
||||
uint64_t lba = req.lba().at(i);
|
||||
uint64_t size = req.sector_cnt().at(i);
|
||||
DmaReadCommand command(lba, size, region_paddr);
|
||||
device->IssueCommand(&command);
|
||||
command.WaitComplete();
|
||||
|
||||
// Wait for read operation to complete.
|
||||
RET_ERR(mutex.Lock());
|
||||
|
||||
curr_run_start = i + 1;
|
||||
region_paddr += size * 512;
|
||||
}
|
||||
|
||||
resp.set_device_id(req.device_id());
|
||||
resp.set_size(req.lba().size());
|
||||
resp.set_size(sector_cnt);
|
||||
resp.set_memory(region.DuplicateCap());
|
||||
return glcr::OK;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,10 @@ message ReadRequest {
|
|||
|
||||
message ReadManyRequest {
|
||||
u64 device_id;
|
||||
// FIXME: Add repeated message fields.
|
||||
// Must be the same length.
|
||||
repeated u64 lba;
|
||||
repeated u64 sector_cnt;
|
||||
}
|
||||
|
||||
message ReadResponse {
|
||||
|
|
|
@ -100,11 +100,21 @@ void ReadManyRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint
|
|||
lba_.PushBack(bytes.At<uint64_t>(i));
|
||||
}
|
||||
|
||||
// Parse sector_cnt.
|
||||
auto sector_cnt_pointer = bytes.At<ExtPointer>(offset + header_size + (8 * 2));
|
||||
|
||||
sector_cnt_.Resize(sector_cnt_pointer.length / sizeof(uint64_t));
|
||||
for (uint64_t i = offset + sector_cnt_pointer.offset;
|
||||
i < offset + sector_cnt_pointer.offset + sector_cnt_pointer.length;
|
||||
i += sizeof(uint64_t)) {
|
||||
sector_cnt_.PushBack(bytes.At<uint64_t>(i));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
uint32_t next_extension = header_size + 8 * 2;
|
||||
uint32_t next_extension = header_size + 8 * 3;
|
||||
const uint32_t core_size = next_extension;
|
||||
// Write device_id.
|
||||
bytes.WriteAt<uint64_t>(offset + header_size + (8 * 0), device_id());
|
||||
|
@ -121,6 +131,19 @@ uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t off
|
|||
uint32_t ext_offset = offset + lba_ptr.offset + (i * sizeof(uint64_t));
|
||||
bytes.WriteAt<uint64_t>(ext_offset, lba().at(i));
|
||||
}
|
||||
// Write sector_cnt.
|
||||
ExtPointer sector_cnt_ptr{
|
||||
.offset = next_extension,
|
||||
.length = (uint32_t)(sector_cnt().size() * sizeof(uint64_t)),
|
||||
};
|
||||
|
||||
next_extension += sector_cnt_ptr.length;
|
||||
bytes.WriteAt<ExtPointer>(offset + header_size + (8 * 2), sector_cnt_ptr);
|
||||
|
||||
for (uint64_t i = 0; i < sector_cnt().size(); i++) {
|
||||
uint32_t ext_offset = offset + sector_cnt_ptr.offset + (i * sizeof(uint64_t));
|
||||
bytes.WriteAt<uint64_t>(ext_offset, sector_cnt().at(i));
|
||||
}
|
||||
|
||||
// The next extension pointer is the length of the message.
|
||||
WriteHeader(bytes, offset, core_size, next_extension);
|
||||
|
@ -129,7 +152,7 @@ uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t off
|
|||
}
|
||||
|
||||
uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
uint32_t next_extension = header_size + 8 * 2;
|
||||
uint32_t next_extension = header_size + 8 * 3;
|
||||
const uint32_t core_size = next_extension;
|
||||
uint64_t next_cap = 0;
|
||||
// Write device_id.
|
||||
|
@ -147,6 +170,19 @@ uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t off
|
|||
uint32_t ext_offset = offset + lba_ptr.offset + (i * sizeof(uint64_t));
|
||||
bytes.WriteAt<uint64_t>(ext_offset, lba().at(i));
|
||||
}
|
||||
// Write sector_cnt.
|
||||
ExtPointer sector_cnt_ptr{
|
||||
.offset = next_extension,
|
||||
.length = (uint32_t)(sector_cnt().size() * sizeof(uint64_t)),
|
||||
};
|
||||
|
||||
next_extension += sector_cnt_ptr.length;
|
||||
bytes.WriteAt<ExtPointer>(offset + header_size + (8 * 2), sector_cnt_ptr);
|
||||
|
||||
for (uint64_t i = 0; i < sector_cnt().size(); i++) {
|
||||
uint32_t ext_offset = offset + sector_cnt_ptr.offset + (i * sizeof(uint64_t));
|
||||
bytes.WriteAt<uint64_t>(ext_offset, sector_cnt().at(i));
|
||||
}
|
||||
|
||||
// The next extension pointer is the length of the message.
|
||||
WriteHeader(bytes, offset, core_size, next_extension);
|
||||
|
|
|
@ -47,10 +47,13 @@ class ReadManyRequest {
|
|||
void set_device_id(const uint64_t& value) { device_id_ = value; }
|
||||
const glcr::Vector<uint64_t>& lba() const { return lba_; }
|
||||
void add_lba(const uint64_t& value) { lba_.PushBack(value); }
|
||||
const glcr::Vector<uint64_t>& sector_cnt() const { return sector_cnt_; }
|
||||
void add_sector_cnt(const uint64_t& value) { sector_cnt_.PushBack(value); }
|
||||
|
||||
private:
|
||||
uint64_t device_id_;
|
||||
glcr::Vector<uint64_t> lba_;
|
||||
glcr::Vector<uint64_t> sector_cnt_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
void ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
add_executable(teton
|
||||
framebuffer/console.cpp
|
||||
framebuffer/framebuffer.cpp
|
||||
framebuffer/psf.cpp
|
||||
teton.cpp
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include "framebuffer/console.h"
|
||||
|
||||
#include <mammoth/debug.h>
|
||||
|
||||
void Console::WriteChar(char c) {
|
||||
if (c == '\n') {
|
||||
CursorReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t row = cursor_pos_ / cols();
|
||||
if (row >= rows()) {
|
||||
crash("Unimplemented console scroll.", glcr::UNIMPLEMENTED);
|
||||
}
|
||||
uint64_t fb_row = row * (psf_.height() + 1);
|
||||
uint64_t col = cursor_pos_ % cols();
|
||||
uint64_t fb_col = col * (psf_.width() + 1);
|
||||
|
||||
uint8_t* glyph = psf_.glyph(c);
|
||||
|
||||
for (uint32_t r = fb_row; r < fb_row + psf_.height(); r++) {
|
||||
for (uint32_t c = fb_col; c < fb_col + psf_.width(); c++) {
|
||||
uint8_t glyph_offset = psf_.width() - (c - fb_col) - 1;
|
||||
if ((glyph[r] & (1 << glyph_offset))) {
|
||||
framebuf_.DrawPixel(r, c, 0xFFFFFFF);
|
||||
} else {
|
||||
framebuf_.DrawPixel(r, c, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor_pos_++;
|
||||
}
|
||||
|
||||
void Console::WriteString(glcr::StringView str) {
|
||||
for (uint64_t i = 0; i < str.size(); i++) {
|
||||
WriteChar(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::CursorReturn() {
|
||||
cursor_pos_ -= cursor_pos_ % cols();
|
||||
cursor_pos_ += cols();
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <glacier/string/string_view.h>
|
||||
|
||||
#include "framebuffer/framebuffer.h"
|
||||
#include "framebuffer/psf.h"
|
||||
|
||||
class Console {
|
||||
public:
|
||||
explicit Console(Framebuffer& fb, Psf& psf) : framebuf_(fb), psf_(psf) {}
|
||||
|
||||
void WriteChar(char c);
|
||||
void WriteString(glcr::StringView str);
|
||||
|
||||
uint32_t rows() { return framebuf_.height() / (psf_.height() + 1); }
|
||||
uint32_t cols() { return framebuf_.width() / (psf_.width() + 1); }
|
||||
|
||||
private:
|
||||
// TODO: Don't store a reference here.
|
||||
Framebuffer& framebuf_;
|
||||
Psf& psf_;
|
||||
uint64_t cursor_pos_ = 0;
|
||||
|
||||
void CursorIncr();
|
||||
void CursorReturn();
|
||||
};
|
|
@ -11,6 +11,9 @@ class Framebuffer {
|
|||
|
||||
void DrawGlyph(uint8_t* glyph);
|
||||
|
||||
uint64_t width() { return fb_info_.width(); }
|
||||
uint64_t height() { return fb_info_.height(); }
|
||||
|
||||
private:
|
||||
// FIXME: Implement Yunq copy or move so we
|
||||
// don't have to store a reference here.
|
||||
|
|
|
@ -20,6 +20,8 @@ class Psf {
|
|||
void DumpHeader();
|
||||
|
||||
uint32_t size() { return header_->numglyph; }
|
||||
uint32_t width() { return header_->width; }
|
||||
uint32_t height() { return header_->height; }
|
||||
|
||||
uint8_t* glyph(uint32_t index) {
|
||||
return reinterpret_cast<uint8_t*>(psf_file_.vaddr() + header_->headersize +
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <victoriafalls/victoriafalls.yunq.client.h>
|
||||
#include <yellowstone/yellowstone.yunq.client.h>
|
||||
|
||||
#include "framebuffer/console.h"
|
||||
#include "framebuffer/framebuffer.h"
|
||||
#include "framebuffer/psf.h"
|
||||
|
||||
|
@ -22,12 +23,6 @@ uint64_t main(uint64_t init_port) {
|
|||
|
||||
Framebuffer fbuf(framebuffer);
|
||||
|
||||
for (uint64_t r = 0; r < 20; r++) {
|
||||
for (uint64_t c = 0; c < 20; c++) {
|
||||
fbuf.DrawPixel(r, c, 0x0000FF00);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Parse a font file.
|
||||
|
||||
GetEndpointRequest req;
|
||||
|
@ -45,7 +40,11 @@ uint64_t main(uint64_t init_port) {
|
|||
Psf psf(OwnedMemoryRegion::FromCapability(fresp.memory()));
|
||||
psf.DumpHeader();
|
||||
|
||||
fbuf.DrawGlyph(psf.glyph('C'));
|
||||
Console console(fbuf, psf);
|
||||
console.WriteString("Hello World!\n");
|
||||
for (uint8_t i = 0x20; i < 0x7E; i++) {
|
||||
console.WriteChar(i);
|
||||
}
|
||||
|
||||
// 3. Write a line to the screen.
|
||||
|
||||
|
|
|
@ -78,14 +78,19 @@ glcr::ErrorOr<OwnedMemoryRegion> Ext2BlockReader::ReadBlocks(
|
|||
const glcr::Vector<uint64_t>& block_list) {
|
||||
ReadManyRequest req;
|
||||
req.set_device_id(device_id_);
|
||||
// FIXME: We should have better ergonomics for setting a repeated field in
|
||||
// Yunq.
|
||||
for (uint64_t i = 0; i < block_list.size(); i++) {
|
||||
uint64_t sector = lba_offset_ + block_list.at(i) * SectorsPerBlock();
|
||||
for (uint64_t j = 0; j < SectorsPerBlock(); j++) {
|
||||
req.add_lba(sector + j);
|
||||
uint64_t curr_start = lba_offset_ + block_list.at(i) * SectorsPerBlock();
|
||||
uint64_t curr_run_len = 1;
|
||||
while ((i + 1) < block_list.size() &&
|
||||
block_list.at(i + 1) == block_list.at(i) + 1) {
|
||||
i++;
|
||||
curr_run_len++;
|
||||
}
|
||||
req.add_lba(curr_start);
|
||||
req.add_sector_cnt(curr_run_len * SectorsPerBlock());
|
||||
dbgln("Read {x}, {x}", curr_start, curr_run_len * SectorsPerBlock());
|
||||
}
|
||||
dbgln("Read many: {x}", req.lba().size());
|
||||
ReadResponse resp;
|
||||
RET_ERR(denali_.ReadMany(req, resp));
|
||||
return OwnedMemoryRegion::FromCapability(resp.memory());
|
||||
|
|
|
@ -31,6 +31,7 @@ add_executable(zion
|
|||
object/port.cpp
|
||||
object/process.cpp
|
||||
object/reply_port.cpp
|
||||
object/semaphore.cpp
|
||||
object/thread.cpp
|
||||
scheduler/context_switch.s
|
||||
scheduler/jump_user_space.s
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
- Reserve lower physical memory for hardware accesses as necessary.
|
||||
- Return errors rather than panicking on allocation failure
|
||||
- Return memory pages to the pool when a MemoryObject goes out of scope.
|
||||
- Add diagnostics to inspect physical memory usage.
|
||||
|
||||
### Virtual Memory
|
||||
|
||||
|
@ -46,7 +44,6 @@
|
|||
|
||||
- Add different scheduling priorities.
|
||||
- Add thread sleep capability.
|
||||
- Add synchronization syscalls for userspace.
|
||||
|
||||
## Large Projects
|
||||
|
||||
|
|
|
@ -64,5 +64,8 @@ SYS1(CapRelease, z_cap_t, cap);
|
|||
SYS1(MutexCreate, z_cap_t*, mutex_cap);
|
||||
SYS1(MutexLock, z_cap_t, mutex_cap);
|
||||
SYS1(MutexRelease, z_cap_t, mutex_cap);
|
||||
SYS1(SemaphoreCreate, z_cap_t*, semaphore_cap);
|
||||
SYS1(SemaphoreWait, z_cap_t, semaphore_cap);
|
||||
SYS1(SemaphoreSignal, z_cap_t, semaphore_cap);
|
||||
|
||||
SYS1(Debug, const char*, message);
|
||||
|
|
|
@ -60,6 +60,9 @@ const uint64_t kZionCapRelease = 0x71;
|
|||
const uint64_t kZionMutexCreate = 0x80;
|
||||
const uint64_t kZionMutexLock = 0x81;
|
||||
const uint64_t kZionMutexRelease = 0x82;
|
||||
const uint64_t kZionSemaphoreCreate = 0x83;
|
||||
const uint64_t kZionSemaphoreWait = 0x84;
|
||||
const uint64_t kZionSemaphoreSignal = 0x85;
|
||||
|
||||
// Debugging Calls.
|
||||
const uint64_t kZionDebug = 0x1'0000;
|
||||
|
@ -89,6 +92,9 @@ const uint64_t kZionPerm_SpawnThread = 0x200;
|
|||
// Permissions on mutexes.
|
||||
const uint64_t kZionPerm_Lock = 0x100;
|
||||
const uint64_t kZionPerm_Release = 0x200;
|
||||
// Permissions on semaphores.
|
||||
const uint64_t kZionPerm_Wait = 0x100;
|
||||
const uint64_t kZionPerm_Signal = 0x200;
|
||||
|
||||
const z_perm_t kZionPerm_None = 0;
|
||||
const z_perm_t kZionPerm_All = -1;
|
||||
|
|
|
@ -75,6 +75,12 @@ extern "C" void interrupt_divide_by_zero(InterruptFrame* frame) {
|
|||
panic("DIV0");
|
||||
}
|
||||
|
||||
extern "C" void isr_invalid_opcode();
|
||||
extern "C" void interrupt_invalid_opcode(InterruptFrame* frame) {
|
||||
dbgln("RIP: {x}", frame->rip);
|
||||
panic("INVALID OPCODE");
|
||||
}
|
||||
|
||||
extern "C" void isr_protection_fault();
|
||||
extern "C" void interrupt_protection_fault(InterruptFrame* frame) {
|
||||
dbgln("General Protection Fault");
|
||||
|
@ -183,6 +189,7 @@ extern "C" void interrupt_pci4(InterruptFrame*) {
|
|||
|
||||
void InitIdt() {
|
||||
gIdt[0] = CreateDescriptor(isr_divide_by_zero);
|
||||
gIdt[0x6] = CreateDescriptor(isr_invalid_opcode);
|
||||
gIdt[0xD] = CreateDescriptor(isr_protection_fault);
|
||||
gIdt[0xE] = CreateDescriptor(isr_page_fault);
|
||||
gIdt[0x13] = CreateDescriptor(isr_fpe_fault);
|
||||
|
|
|
@ -56,6 +56,7 @@ isr_\name:
|
|||
.endm
|
||||
|
||||
isr_handler divide_by_zero
|
||||
isr_handler invalid_opcode
|
||||
isr_handler protection_fault,1
|
||||
isr_handler page_fault,1
|
||||
isr_handler fpe_fault
|
||||
|
|
|
@ -15,6 +15,7 @@ class KernelObject : public glcr::RefCounted<KernelObject> {
|
|||
ENDPOINT = 0x7,
|
||||
REPLY_PORT = 0x8,
|
||||
MUTEX = 0x9,
|
||||
SEMAPHORE = 0x10,
|
||||
};
|
||||
|
||||
virtual uint64_t TypeTag() = 0;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#include "object/semaphore.h"
|
||||
|
||||
#include "scheduler/scheduler.h"
|
||||
|
||||
glcr::RefPtr<Semaphore> Semaphore::Create() {
|
||||
return glcr::AdoptPtr<Semaphore>(new Semaphore());
|
||||
}
|
||||
|
||||
// FIXME: We almost certainly have some race conditions
|
||||
// between this and unlock where we could end up with
|
||||
// a thread in the blocked_threads_ queue while noone is holding the lock.
|
||||
void Semaphore::Signal() {
|
||||
__atomic_fetch_add(&lock_, 0x1, __ATOMIC_SEQ_CST);
|
||||
if (blocked_threads_.size() > 0) {
|
||||
auto thread = blocked_threads_.PopFront();
|
||||
thread->SetState(Thread::RUNNABLE);
|
||||
gScheduler->Enqueue(thread);
|
||||
}
|
||||
}
|
||||
|
||||
void Semaphore::Wait() {
|
||||
while (lock_ == 0) {
|
||||
auto thread = gScheduler->CurrentThread();
|
||||
thread->SetState(Thread::BLOCKED);
|
||||
blocked_threads_.PushBack(thread);
|
||||
gScheduler->Yield();
|
||||
}
|
||||
__atomic_fetch_sub(&lock_, 0x1, __ATOMIC_SEQ_CST);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <glacier/container/intrusive_list.h>
|
||||
#include <glacier/memory/ref_ptr.h>
|
||||
|
||||
#include "include/ztypes.h"
|
||||
#include "object/kernel_object.h"
|
||||
#include "object/thread.h"
|
||||
|
||||
class Semaphore;
|
||||
|
||||
template <>
|
||||
struct KernelObjectTag<Semaphore> {
|
||||
static const uint64_t type = KernelObject::SEMAPHORE;
|
||||
};
|
||||
|
||||
class Semaphore : public KernelObject {
|
||||
public:
|
||||
uint64_t TypeTag() override { return KernelObject::SEMAPHORE; }
|
||||
static uint64_t DefaultPermissions() {
|
||||
return kZionPerm_Wait | kZionPerm_Signal;
|
||||
}
|
||||
|
||||
static glcr::RefPtr<Semaphore> Create();
|
||||
|
||||
void Wait();
|
||||
void Signal();
|
||||
|
||||
private:
|
||||
uint8_t lock_ = 0;
|
||||
|
||||
glcr::IntrusiveList<Thread> blocked_threads_;
|
||||
};
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
z_err_t ProcessExit(ZProcessExitReq* req) {
|
||||
auto curr_thread = gScheduler->CurrentThread();
|
||||
dbgln("Exit code: {x}", req->code);
|
||||
dbgln("Exit code: {}", static_cast<glcr::ErrorCode>(req->code));
|
||||
curr_thread->process().Exit();
|
||||
panic("Returned from thread exit");
|
||||
return glcr::UNIMPLEMENTED;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "syscall/synchronization.h"
|
||||
|
||||
#include "object/mutex.h"
|
||||
#include "object/semaphore.h"
|
||||
#include "scheduler/scheduler.h"
|
||||
|
||||
glcr::ErrorCode MutexCreate(ZMutexCreateReq* req) {
|
||||
|
@ -22,10 +23,35 @@ glcr::ErrorCode MutexLock(ZMutexLockReq* req) {
|
|||
glcr::ErrorCode MutexRelease(ZMutexReleaseReq* req) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
auto cap = curr_proc.GetCapability(req->mutex_cap);
|
||||
// TODO: We may not want a separate permission for releasing the mutex.
|
||||
RET_ERR(ValidateCapability<Mutex>(cap, kZionPerm_Release));
|
||||
|
||||
auto mutex = cap->obj<Mutex>();
|
||||
mutex->Release();
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
glcr::ErrorCode SemaphoreCreate(ZSemaphoreCreateReq* req) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
*req->semaphore_cap = curr_proc.AddNewCapability(Semaphore::Create());
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
glcr::ErrorCode SemaphoreWait(ZSemaphoreWaitReq* req) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
auto cap = curr_proc.GetCapability(req->semaphore_cap);
|
||||
RET_ERR(ValidateCapability<Semaphore>(cap, kZionPerm_Wait));
|
||||
|
||||
auto semaphore = cap->obj<Semaphore>();
|
||||
semaphore->Wait();
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
glcr::ErrorCode SemaphoreSignal(ZSemaphoreSignalReq* req) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
auto cap = curr_proc.GetCapability(req->semaphore_cap);
|
||||
RET_ERR(ValidateCapability<Semaphore>(cap, kZionPerm_Signal));
|
||||
|
||||
auto semaphore = cap->obj<Semaphore>();
|
||||
semaphore->Signal();
|
||||
return glcr::OK;
|
||||
}
|
||||
|
|
|
@ -7,3 +7,7 @@
|
|||
glcr::ErrorCode MutexCreate(ZMutexCreateReq* req);
|
||||
glcr::ErrorCode MutexLock(ZMutexLockReq* req);
|
||||
glcr::ErrorCode MutexRelease(ZMutexReleaseReq* req);
|
||||
|
||||
glcr::ErrorCode SemaphoreCreate(ZSemaphoreCreateReq* req);
|
||||
glcr::ErrorCode SemaphoreWait(ZSemaphoreWaitReq* req);
|
||||
glcr::ErrorCode SemaphoreSignal(ZSemaphoreSignalReq* req);
|
||||
|
|
|
@ -88,6 +88,9 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req) {
|
|||
CASE(MutexCreate);
|
||||
CASE(MutexLock);
|
||||
CASE(MutexRelease);
|
||||
CASE(SemaphoreCreate);
|
||||
CASE(SemaphoreWait);
|
||||
CASE(SemaphoreSignal);
|
||||
// syscall/debug.h
|
||||
CASE(Debug);
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue