[magenta] Initial commit

Esse commit está contido em:
The Fuchsia Authors
2016-06-15 00:31:24 -07:00
commit de Brian Swetland
commit 53b9e1c8de
3454 arquivos alterados com 484582 adições e 0 exclusões
+13
Ver Arquivo
@@ -0,0 +1,13 @@
Language: Cpp
ColumnLimit: 0
UseTab: Never
IndentWidth: 4
BreakBeforeBraces: Attach
AccessModifierOffset: -4
DerivePointerAlignment: false
PointerAlignment: Left
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
KeepEmptyLinesAtTheStartOfBlocks: true
AlignEscapedNewlinesLeft: false
ForEachMacros: ['list_for_every_entry','list_for_every_entry_safe']
+9
Ver Arquivo
@@ -0,0 +1,9 @@
;; Emacs settings.
;; Copyright (C) 2016 Google, Inc.
(
(c-mode . ((c-file-style . "stroustrup")
(indent-tabs-mode . nil)))
(c++-mode . ((c-file-style . "stroustrup")
(indent-tabs-mode . nil)))
)
+25
Ver Arquivo
@@ -0,0 +1,25 @@
build-*/
*.swp
*~
tags
TAGS
local.mk
*.orig
cscope.*
doit
t
tagit
blk.bin
/gn
*.dSYM
scripts/toolpaths.local
clang-format
*_BACKUP_*
*_REMOTE_*
*_LOCAL_*
*_BASE_*
*.sublime-*
/out*
/tools/gn/bin/linux
/tools/gn/bin/mac
/tools/gn/bin/win
+2
Ver Arquivo
@@ -0,0 +1,2 @@
# The location of the build configuration file.
buildconfig = "//gnbuild/BUILDCONFIG.gn"
+8
Ver Arquivo
@@ -0,0 +1,8 @@
# This is the list of Fuchsia Authors.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
# Individual's name <submission email address> <email2> <emailN>
Google Inc.
+48
Ver Arquivo
@@ -0,0 +1,48 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
import("//gnbuild/convert_to_raw.gni")
# This target will be built if no target is specified when invoking Ninja.
executable("lk") {
output_extension = "elf"
configs -= [ "//gnbuild:user_defaults" ]
configs += [ "//gnbuild:kernel_defaults" ]
deps = [
"//kernel/top",
]
if (target_cpu == "x64") {
deps += [
"//kernel/arch/x86",
"//kernel/arch/x86:kernel_ld",
]
if (target_platform == "pc") {
deps += [ "//kernel/project:pc-x86-64" ]
} else if (target_platform == "qemu") {
deps += [ "//kernel/project:qemu-x86-64" ]
}
} else if (target_cpu == "arm") {
deps += [
"//kernel/arch/arm",
"//kernel/arch/arm:onesegment_ld",
"//kernel/project:qemu-arm32",
]
} else if (target_cpu == "arm64") {
deps += [
"//kernel/arch/arm64",
"//kernel/arch/arm64:onesegment_ld",
"//kernel/project:qemu-arm64",
]
}
}
convert_to_raw("lk_bin") {
input = "${root_build_dir}/lk.elf"
output = "${root_build_dir}/lk.bin"
deps = [
":lk",
]
}
+9
Ver Arquivo
@@ -0,0 +1,9 @@
The Magenta Kernel (kernel/...) is under the MIT License, a copy of which
may be found in kernel/LICENSE
The Magenta System (system/...) is under the Apache 2 License, a copy of
which may be found in system/LICENSE
The Third Party Components (third_party/...) are under various licenses
(of the BSD, MIT, or Zlib style), which may be found in the root directory
of each component, respectively.
+8
Ver Arquivo
@@ -0,0 +1,8 @@
# MAGENTA REPOSITORY
Welcome to the Magenta repository.
The documentation is divided into
+ [LK](docs/lk/index.md) specific.
+ [Magenta](docs/magenta/index.md) specific.
+22
Ver Arquivo
@@ -0,0 +1,22 @@
#LK
The LK embedded kernel. An SMP-aware kernel designed for small systems.
See https://github.com/littlekernel/lk for the latest version.
See https://github.com/littlekernel/lk/wiki for documentation.
## to build and test for ARM on linux
1. install or build qemu. v2.4 and above is recommended.
2. install gcc for embedded arm (see note 1)
3. run scripts/do-qemuarm (from the lk directory)
4. you should see 'welcome to lk/MP'
This will get you a interactive prompt into LK which is running in qemu
arm machine 'virt' emulation. type 'help' for commands.
note 1: for ubuntu:
sudo apt-get install gcc-arm-none-eabi
or fetch a prebuilt toolchain from
http://newos.org/toolchains/arm-eabi-5.3.0-Linux-x86_64.tar.xz
+76
Ver Arquivo
@@ -0,0 +1,76 @@
# Futexes
## What is a futex?
A **futex** is a Fast Userspace muTEX. It is a low level
synchronization primitive which is a building block for higher level
APIs such as `pthread_mutex_t` and `pthread_cond_t`.
Futexes are designed to not enter the kernel or allocate kernel
resources in the uncontested case.
## Papers about futexes
- [Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux](https://www.kernel.org/doc/ols/2002/ols2002-pages-479-495.pdf), Hubertus Franke and Rusty Russell
This is the original white paper describing the Linux futex. It
documents the history and design of the original implementation,
prior (failed) attempts at creating a fast userspace
synchronization primitive, and performance measurements.
- [Futexes Are Tricky](https://www.akkadia.org/drepper/futex.pdf), Ulrich Drepper
This paper describes some gotchas and implementation details of
futexes in Linux. It discusses the kernel implementation, and goes
into more detail about correct and efficient userspace
implementations of mutexes, condition variables, and so on.
## API
The magenta futex implementation currently supports three operations:
```C
uint32_t sys_futex_wait(int* value_ptr, int current_value, uint32_t timeout)
uint32_t sys_futex_wake(int* value_ptr, uint32_t wake_count)
uint32_t sys_futex_requeue(int* value_ptr, uint32_t wake_count, int current_value, int* requeue_ptr, uint32_t requeue_count);
```
All of these share a `value_ptr` parameter, which is the virtual
address of an aligned userspace integer. This virtual address is the
information used in kernel to track what futex given threads are
waiting on. The kernel does not currently modify the value of
`*value_ptr` (but see below for future operations which might do
so). It is up to userspace code to correctly atomically modify this
value across threads in order to build mutexes and so on.
### `sys_futex_wait`
Waiting on a futex (or acquiring it) causes a thread to sleep until
the futex is made available by a call to `sys_futex_wake`. Optionally,
the thread can also be woken up after the timeout argument expires.
### `sys_futex_wake`
Waking a futex causes `wake_count` threads waiting on that futex to be
woken up.
### `sys_futex_requeue`
Requeuing is a generalization of waking. After waking `wake_count`
threads, `requeue_count` threads are moved from the original futex's
wait queue to the wait queue corresponding to `requeue_ptr`, another
futex.
This requeueing behavior is used to avoid thundering herds on wake.
## Differences from Linux futexes
Note that all of the magenta futex operations key off of the virtual
address of an userspace pointer. This differs from the Linux
implementation, which distinguishes private futex operations (which
correspond to our in-process-only ones) from ones shared across
address spaces.
As noted above, all of our futex operations leave the value of the
futex unmodified from the kernel. Other potential operations, such as
Linux's `FUTEX_WAKE_OP`, requires atomic manipulation of the value
from the kernel, which our current implementation does not require.
+107
Ver Arquivo
@@ -0,0 +1,107 @@
# GN
## Prerequisites
1. run `sudo apt-get install ninja-build` to install Ninja
2. build toolchains for aarch64 and x86_64:
```bash
cd $SRC
git clone https://github.com/travisg/toolchains.git
cd toolchains
./doit -a 'arm i686 aarch64 x86_64' -f -j32
export PATH=$SRC/toolchains/aarch64-elf-5.3.0-Linux-x86_64/bin:$SRC/toolchains/x86_64-elf-5.3.0-Linux-x86_64/bin:$PATH
```
3. build and install qemu:
```bash
cd $SRC
git clone http://git.qemu.org/git/qemu.git
cd qemu
./configure --target-list=aarch64-softmmu,x86_64-softmmu
make -j32
```
4. run `./scripts/download-gn` (from the lk directory) to download GN
## Build Magenta with GN for QEMU x64
1. run GN to generate Ninja build script:
```bash
./gn gen out/qemu-x64 --script-executable=/bin/sh --args='target_cpu="x64" target_platform="qemu"'
```
2. run Ninja to build the Magenta:
```bash
ninja -C out/qemu-x64
```
3. run Magenta using qemu:
```bash
./scripts/run-magenta-x86-64 -o out/qemu-x64
```
## Build Magenta with GN for QEMU arm64
1. run GN to generate Ninja build script:
```bash
./gn gen out/qemu-arm64 --script-executable=/bin/sh --args='target_cpu="arm64" target_platform="qemu"'
```
2. run Ninja to build the Magenta:
```bash
ninja -C out/qemu-arm64
```
3. run Magenta using qemu:
```bash
./scripts/run-magenta-arm64 -o out/qemu-arm64
```
## Build Magenta with GN for Pixel 2
1. run GN to generate Ninja build script:
```bash
./gn gen out/pc-x64 --script-executable=/bin/sh --args='target_cpu="x64" target_platform="pc"'
```
2. run Ninja to build the Magenta:
```bash
ninja -C out/pc-x64
```
3. run Magenta using network boot:
```bash
./tools/bootserver out/pc-x64/lk.bin
```
## Build Magenta with GN for QEMU arm32
1. run GN to generate Ninja build script:
```bash
./gn gen out/qemu-arm32 --script-executable=/bin/sh --args='target_cpu="arm32" target_platform="qemu"'
```
2. run Ninja to build the Magenta:
```bash
ninja -C out/qemu-arm32
```
3. run Magenta using qemu:
```bash
./scripts/run-magenta-arm32 -o out/qemu-arm32
```
+14
Ver Arquivo
@@ -0,0 +1,14 @@
# Magenta
Magenta is the OS kernel that powers FNL for Phones and
Personal Computers.
This page is a non-comprehensive index of the magenta documentation.
+ [Relationship with LK](mg_and_lk.md)
+ [Kernel Objects](kernel_objects.md)
+ [Process Objects](process_object.md)
+ [Thread Objects](thread_object.md)
+ [Futexes](futex.md)
+ [System Calls](syscalls.md)
+ [Build System](gn.md)
+54
Ver Arquivo
@@ -0,0 +1,54 @@
# Magenta Kernel objects
[TOC]
Magenta is a object-based kernel. User mode code almost exclusively interacts
with OS resources via object handles which map kernel objects to processes.
## Kernel objects in progress
+ [Process](process_object.md)
+ [Thread](thread_object.md)
+ Event
+ Message pipe
+ Interrupt request
+ Futex
## Kernel objects planned
+ Data pipe
+ Memory mapping (VM object)
+ Job
## Kernel Object and LK
Most kernel objects wrap one or more LK-level constructs. For example the
Thread object wraps one thread_t, or the Event object wraps one event_t.
The Message Pipe however wraps two port_t objects and there is not a fixed
mapping from LK to the Process object.
## Kernel object lifetime
Kernel objects are ref-counted. Most kernel objects are born during a syscall
and are held alive at refcount = 1 by the handle which binds the handle value
given as the output of the syscall. The handle object is held alive as long it
is attached to a handle table. Handles are detached from the handle table
closing them (for example via `sys_close()`) which decrements the refcount of
the kernel object. Usually, when the last handle is closed the kernel object
refcount will reach 0 which causes the destructor to be run.
The refcount increases both when new handles (referring to the object) are
created and when a direct pointer reference (by some kernel code) is acquired;
therefore a kernel object lifetime might be longer than the lifetime of the
process that created it.
## Dispatchers
A kernel object is implemented as a C++ class that derives from `Dispatcher`
and that overrides the methods it implements. Thus, for example, the code
of the Thread object is found in `ThreadDispatcher`. There is plenty of
code that only cares about kernel objects in the generic sense, in that case
the name you'll see is `utils::RefPtr<Dispatcher>`.
## Kernel Object security
In principle, kernel objects do not have an intrinsic notion of security and
do not do authorization; security rights are held by each handle. A Single
process can have two different handles to the same object with different
rights.
+22
Ver Arquivo
@@ -0,0 +1,22 @@
# Magenta and LK
Magenta is based on [LK](https://github.com/littlekernel/lk) but over time it
will diverge from it significantly. As it does this document should be updated
accordingly.
LK is a Kernel designed for small systems typically used in embedded
applications. It is good alternative to commercial offerings like
[FreeRTOS](http://www.freertos.org/) or [ThreadX](http://rtos.com/products/threadx/).
Such systems often have a very limited amount of ram, a fixed set of peripherals
and a bounded set of tasks.
On the other hand, Magenta targets modern phones and modern personal computers
with fast processors, non-trivial amounts of ram with arbitrary peripherals
doing open ended computation.
More specifically, some the visible differences are:
+ Magenta has user-mode support `lib/lkuser, lib/magenta`, upstream LK does not.
+ Magenta has objects, they are manipulated by user mode via handles.
+ Magenta has a capability-based security model. In LK all code is trusted.
+ Magenta supports at the kernel level, the Mojo application model.
+22
Ver Arquivo
@@ -0,0 +1,22 @@
# Process Object
The process object, just like on other OSes is a container of the following
resources:
+ Handle table
+ Memory regions
+ [Threads](thread_object.md)
In general it operates more or less like a Linux or Windows process in
terms of being it associated with code (one or more ELF binaries) which it
is executing until it is forcefully terminated or the program exits.
## Lifetime
A process is created via `sys_process_create()` which take no parameters.
The process starts destruction when main thread terminates or the last handle
is closed. [⚠ not implemented].
Next, the main binary is loaded into the process via `sys_process_load()` and
its execution begins with `sys_process_start()`.
A thread in another process can wait for a process to exit with
`sys_process_join()`.
+14
Ver Arquivo
@@ -0,0 +1,14 @@
# Magenta System Calls
## Handles
+ [handle_close](syscalls/handle_close.md)
+ [handle_duplicate](syscalls/handle_duplicate.md)
+ [handle_wait_many](syscalls/handle_wait_many.md)
+ [handle_wait_one](syscalls/handle_wait_one.md)
## Message Pipes
+ [message_pipe_create](syscalls/message_pipe_create.md)
+ [message_read](syscalls/message_read.md)
+ [message_write](syscalls/message_write.md)
+27
Ver Arquivo
@@ -0,0 +1,27 @@
# _magenta_handle_close
## NAME
handle_close - close a handle
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_status_t _magenta_handle_close(mx_handle_t handle);
```
## DESCRIPTION
**handle_close**() closes a *handle*, causing the underlying object to be
reclaimed by the kernel if no other handles to it exist.
## RETURN VALUE
**handle_close**() returns **NO_ERROR** on success.
## ERRORS
**ERR_INVALID_ARGS** *handle* isn't a valid handle.
+33
Ver Arquivo
@@ -0,0 +1,33 @@
# _magenta_handle_duplicate
## NAME
handle_duplicate - duplicate a handle
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_handle_t _magenta_handle_duplicate(mx_handle_t handle);
```
## DESCRIPTION
**handle_duplicate**() creates a duplicate of *handle*, referring
to the same underlying object, suitable for passing to another process
via a message pipe.
## RETURN VALUE
**handle_duplicate**() returns the duplicate handle on success (a
positive value), or an error code (negative).
## ERRORS
**ERR_INVALID_ARGS** *handle* isn't a valid handle.
**ERR_ACCESS_DENIED** *handle* does not have **MX_RIGHT_DUPLICATE** and may not be duplicated.
**ERR_NO_MEMORY** (Temporary) out of memory situation.
+70
Ver Arquivo
@@ -0,0 +1,70 @@
# _magenta_handle_wait_many
## NAME
handle_wait_many - wait for signals on multiple handles
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_status_t _magenta_handle_wait_many(uint32_t count, const mx_handle_t* handles,
const mx_signals_t* signals,
mx_time_t timeout,
mx_signals_t* satisfied_signals,
mx_signals_t* satisfiable_signals);
```
## DESCRIPTION
**handle_wait_many**() is a blocking syscall which causes the caller to
wait until at least one of the specified *signals* is pending on one of
the specified handle *handles* or *timeout* elapses.
The caller must provide *count* handles in the *handles* array and *count*
signal bitmasks in the *signals* array. For each entry in the *handles*
array, the corresponding entry in *signals* indicates which signals the
calling thread should be resumed for.
The *timeout* parameter is relative time (from now) in nanoseconds which
takes two special values: **0** and **MX_TIME_INFINITE**. The former causes
the wait to complete immediately and the latter signals that wait will
never times out.
Upon return, if non-NULL, the *satisfied_signals* array is filled with
a bitmap for each of the *count* specified handles, indicating which
signals are pending on that particular handle.
It is possible to have the call return with a *satisfiable_signals* array
with values different than the values that caused the wait to complete
if other threads are further modifing the objects behind the *handles*.
If non-NULL, the *satisfiable_signals* is filled with a bitmap for each of
the specified handles indicating which signals are possible to be pending
on that handle, given its type and current state.
## RETURN VALUE
**handle_wait_many**() returns **NO_ERROR** on success when the wait was
satisfied by the *signals* input or **ERR_TIMED_OUT** if the wait completed
because *timeout* nanoseconds have elapsed.
In the event of **ERR_TIMED_OUT**, *satisfied_signals* may indicate
signals that were satisfied after the timeout but before the syscall
returned.
## ERRORS
**ERR_INVALID_ARGS** *handle* isn't a valid handle or *satisfied_signals*
or *satisfiable_signals* were invalid pointers.
**ERR_ACCESS_DENIED** One or more of the provided *handles* does not
have **MX_RIGHT_READ** and may not be waited upon.
**ERR_NO_MEMORY** (Temporary) failure due to lack of memory.
## BUGS
Currently the *satisfiable_signals* array is filled with the same content
as the *satisifed_signals* array.
+58
Ver Arquivo
@@ -0,0 +1,58 @@
# _magenta_handle_wait_one
## NAME
handle_wait_one - wait for signals on a handle
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_status_t _magenta_handle_wait_one(mx_handle_t handle, mx_signals_t signals,
mx_signals_t* satisfied_signals,
mx_time timeout,
mx_signals_t* satisfiable_signals);
```
## DESCRIPTION
**handle_wait_one**() is a blocking syscall which causes the caller to
wait until at least one of the specified *signals* is pending on *handle*
or *timeout* elapses.
Upon return, if non-NULL, *satisfied_signals* is a bitmap of all of the
signals which are pending on *handle* and *satisfiable_signals* is
a bitmap of all of the signals which are possible to be pending on
*handle*, given its type and current state.
It is possible to have the call return with a *satisfiable_signals*
value being different than the value that caused the wait to complete
if another thread is further modifing the object behind *handle*.
The *timeout* parameter is relative time (from now) in nanoseconds which
takes two special values: **0** and **MX_TIME_INFINITE**. The former causes
the wait to complete immediately and the latter signals that wait will
never times out.
## RETURN VALUE
**handle_wait_one**() returns **NO_ERROR** on success or **ERR_TIMED_OUT**
if the wait completed because *timeout* nanoseconds have elapsed.
In the event of **ERR_TIMED_OUT**, *satisfied_signals* may indicate
signals that were satisfied after the timeout but before the syscall
returned.
## ERRORS
**ERR_INVALID_ARGS** *handle* isn't a valid handle or *satisfied_signals*
or *satisfiable_signals* were invalid pointers.
**ERR_ACCESS_DENIED** *handle* does not have **MX_RIGHT_READ** and may
not be waited upon.
## BUGS
Currently the *satisfiable_signals* return value is filled with the same
content as the *satisifed_signals* value.
+53
Ver Arquivo
@@ -0,0 +1,53 @@
# _magenta_message_pipe_create
## NAME
message_pipe_create - create a message pipe
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_handle_t _magenta_message_pipe_create(mx_handle_t* other_handle);
```
## DESCRIPTION
**message_pipe_create**() creates a message pipe, a bi-directional
datagram-style message transport capable of sending raw data bytes
as well as handles from one side to the other.
Two handles are returned on success, providing access to both sides
of the message pipe. Messages written to one handle may be read
from the opposite.
The handles will have MX_RIGHT_TRANSFER (allowing them to be sent
to another process via message pipe write), MX_RIGHT_WRITE (allowing
messages to be written to them), and MX_RIGHT_READ (allowing messages
to be read from them).
## RETURN VALUE
**message_pipe_create**() returns a valid message pipe handle (positive)
on success, in which case the handle to the other side of the message
pipe is returned via the *other_handle* pointer. In the event of failure,
a negative error value is returned. Zero (the "invalid handle") is never
returned.
## ERRORS
**ERR_INVALID_ARGS** *other_handle* is an inavlid pointer or NULL.
**ERR_NO_MEMORY** (Temporary) Failure due to lack of memory.
## SEE ALSO
[handle_close](handle_close.md),
[handle_duplicate](handle_duplicate.md),
[handle_wait_one](handle_wait_one),
[handle_wait_many](handle_wait_many.md),
[message_read](message_read.md),
[message_write](message_write.md).
+76
Ver Arquivo
@@ -0,0 +1,76 @@
# _magenta_message_read
## NAME
message_read - read a message from a message pipe
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_status_t _magenta_message_read(mx_handle_t handle,
void* bytes, uint32_t* num_bytes,
mx_handle_t* handles, uint32_t* num_handles,
uint32_t flags);
```
## DESCRIPTION
**message_read**() attempts to read the first message from the message
pipe specified by *handle* into the provided *bytes* and/or *handles*
buffers.
The pointers *num_bytes* and *num_handles* are used to specify the
size of the read buffers, and upon return may be used to indicate the
actual size of the message read (upon success) or the size of the
message (upon failure due to the provided buffers being too small.
If these pointers are NULL, their respective buffer sizes are understood
to be zero.
Message pipe messages may contain both byte data and handle payloads
and may only be read in their entirety. Partial reads are not possible.
## RETURN VALUE
**message_read**() returns **NO_ERROR** on success, and the uint32_t's
pointed at by *num_bytes* and/or *num_handles* (provided they are
non-NULL) are updated to reflect the exact size of the byte and handle
payloads of the message read.
## ERRORS
**ERR_INVALID_ARGS** *handle* isn't a valid message pipe handle, or
*num_bytes* (if non-NULL) is an invalid pointer, or *num_handles* (if
non-NULL) is an invalid pointer, or *bytes* is non-NULL but
*num_bytes* is NULL, or *handles* is non-NULL but *num_handles*
is null, or *handles* or *num_handles* (if non-NULL) are invalid
pointers.
**ERR_ACCESS_DENIED** *handle* does not have **MX_RIGHT_READ**.
**ERR_NO_MSG** The message pipe contained no messages to read.
**ERR_BAD_STATE** The other side of the message pipe is closed.
**ERR_NO_MEMORY** (Temporary) Failure due to lack of memory.
**ERR_NOT_ENOUGH_BUFFER** The provided *bytes* or *handles* buffers
are too small (in which case, the minimum sizes necessary to receive
the message will be written to the uint32_t's pointed at by these
parameters, provided they are non-NULL).
## NOTES
*num_handles* is a pointer to a count of the number of elements in
the *handles* array, not its size in bytes.
## SEE ALSO
[handle_close](handle_close.md),
[handle_duplicate](handle_duplicate.md),
[handle_wait_one](handle_wait_one),
[handle_wait_many](handle_wait_many.md),
[message_pipe_create](message_pipe_create.md),
[message_write](message_write.md).
+58
Ver Arquivo
@@ -0,0 +1,58 @@
# _magenta_message_write
## NAME
message_write - write a message to a message pipe
## SYNOPSIS
```
#include <magenta/syscalls.h>
mx_status_t _magenta_message_write(mx_handle_t handle,
void* bytes, uint32_t num_bytes,
mx_handle_t* handles, uint32_t num_handles,
uint32_t flags);
```
## DESCRIPTION
**message_write**() attempts to write a message of *num_bytes*
bytes and *num_handles* handles to the message pipe specified by
*handle*. The pointers *handles* and *bytes* may be null if their
respective sizes are zero.
## RETURN VALUE
**message_write**() returns **NO_ERROR** on success.
## ERRORS
**ERR_INVALID_ARGS** *handle* isn't a valid message pipe handle, or
*bytes* is an invalid pointer, or *handles* is an invalid pointer,
or any of the handles passed via the *handles* array are invalid
handles.
**ERR_ACCESS_DENIED** *handle* does not have **MX_RIGHT_WRITE**.
**ERR_BAD_STATE** The other side of the message pipe is closed.
**ERR_NO_MEMORY** (Temporary) Failure due to lack of memory.
**ERR_TOO_BIG** *num_bytes* or *num_handles* are larger than the
largest allowable size for message pipe messages.
## NOTES
*num_handles* is a count of the number of elements in the *handles*
array, not its size in bytes.
## SEE ALSO
[handle_close](handle_close.md).
[handle_duplicate](handle_duplicate.md),
[handle_wait_one](handle_wait_one),
[handle_wait_many](handle_wait_many.md),
[message_pipe_create](message_pipe_create.md),
[message_read](message_read.md).
+14
Ver Arquivo
@@ -0,0 +1,14 @@
# Thread
Just like in other OSes, the thread object is the construct that represents a
time-shared CPU execution context. Thread objects live associated to a
particular [Process Object](process_object.md) which provides the memory and
the handles to other objects necessary for I/O and computation.
## Lifetime
A thread can be created implicitly by calling `sys_process_start()`, in which
case the new thread is the "main thread" and the thread entrypoint is defined by
the previously loaded ELF binary. Or it can be created by calling
`sys_thread_create()` which takes the entrypoint as a parameter.
A thread terminates when it `return`s from executing the routine specified as
the entrypoint or by calling `sys_thread_exit()`.
+318
Ver Arquivo
@@ -0,0 +1,318 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
import("//gnbuild/config.gni")
config("compiler_defaults") {
cflags = [
"-O2",
"-finline",
"-ffunction-sections",
"-fdata-sections",
]
cflags_c = [ "-std=c11" ]
cflags_cc = [
"-std=c++11",
"-fno-exceptions",
"-fno-asynchronous-unwind-tables",
"-fno-rtti",
"-fno-threadsafe-statics",
]
if (target_cpu == "x64") {
defines = [
"ARCH_X86=1",
"ARCH_X86_64=1",
"ARCH_X86_32=0",
"IS_64BIT=1",
"ARCH=\"x86\"",
"PLATFORM=\"PC\"",
"PLATFORM_PC=1",
"PLATFORM_HAS_DYNAMIC_TIMER=1",
"X86_WITH_FPU=1",
]
if (target_platform == "qemu") {
defines += [
"PROJECT_MAGENTA_QEMU_X86_64=1",
"PROJECT=\"MAGENTA_QEMU_X86_64\"",
"TARGET_QEMU_X86_64=1",
"TARGET=\"QEMU_X86_64\"",
]
} else if (target_platform == "pc") {
defines += [
"PROJECT_MAGENTA_PC_UEFI=1",
"PROJECT=\"MAGENTA_PC_UEFI\"",
"TARGET_PC_UEFI=1",
"TARGET=\"PC_UEFI\"",
]
}
cflags += [
"-fno-pic",
"-fno-stack-protector",
]
if (is_clang) {
cflags += [
"-target",
"x86_64-elf",
]
}
} else if (target_cpu == "arm") {
defines = [
"__thumb__",
"ARCH_ARM=1",
"ARM_CPU_CORTEX_A15=1",
"ARM_WITH_CP15=1",
"ARM_WITH_MMU=1",
"ARM_ISA_ARMV7=1",
"ARM_ISA_ARMV7A=1",
"ARM_WITH_THUMB=1",
"ARM_WITH_THUMB2=1",
"ARM_WITH_CACHE=1",
"ARM_WITH_L2=1",
"ARM_WITH_VFP=1",
"ARM_WITH_NEON=1",
"ARCH_DEFAULT_STACK_SIZE=4096",
"PLATFORM_HAS_DYNAMIC_TIMER=1",
"PLATFORM_SUPPORTS_PANIC_SHELL=1",
"MMU_WITH_TRAMPOLINE=1",
"ARCH=\"arm\"",
"PLATFORM_QEMU_VIRT=1",
"PLATFORM=\"QEMU_VIRT\"",
"PROJECT_MAGENTA_QEMU_ARM32=1",
"PROJECT=\"MAGENTA_QEMU_ARM32\"",
"TARGET_QEMU_VIRT=1",
"TARGET=\"QEMU_VIRT\"",
]
cflags += [
"-mthumb-interwork",
"-mcpu=cortex-a15",
"-mfpu=vfpv3",
"-mfloat-abi=softfp",
"-mthumb",
]
asmflags = [
"-mthumb-interwork",
"-mcpu=cortex-a15",
"-mfpu=vfpv3",
"-mfloat-abi=softfp",
"-mthumb",
]
if (is_clang) {
cflags += [
"-target",
"arm-elf",
]
}
} else if (target_cpu == "arm64") {
defines = [
"ARM64_CPU_CORTEX_A53=1",
"ARCH_ARM64=1",
"ARM_ISA_ARMV8=1",
"ARM_ISA_ARMV8A=1",
"IS_64BIT=1",
"ARCH_DEFAULT_STACK_SIZE=4096",
"PLATFORM_HAS_DYNAMIC_TIMER=1",
"PLATFORM_SUPPORTS_PANIC_SHELL=1",
"MMU_WITH_TRAMPOLINE=1",
"ARCH=\"arm64\"",
"ARM_CPU=\"cortex-a53\"",
"PLATFORM_QEMU_VIRT=1",
"PLATFORM=\"QEMU_VIRT\"",
"PROJECT_MAGENTA_QEMU_ARM64=1",
"PROJECT=\"MAGENTA_QEMU_ARM64\"",
"TARGET_QEMU_VIRT=1",
"TARGET=\"QEMU_VIRT\"",
]
if (is_clang) {
cflags += [
"-target",
"aarch64-elf",
]
}
}
}
config("no_floats") {
defines = [ "WITH_NO_FP=1" ]
if (target_cpu == "x64") {
cflags = [
"-msoft-float",
"-mno-mmx",
"-mno-sse",
"-mno-sse2",
"-mno-3dnow",
"-mno-avx",
"-mno-avx2",
]
if (!is_clang) {
cflags += [
"-mno-80387",
"-mno-fp-ret-in-387",
]
}
}
}
config("kernel_defaults") {
configs = [ ":no_floats" ]
include_dirs = [ "//kernel/include" ]
defines = [
"LK=1",
"_KERNEL",
]
cflags = [ "-ffreestanding" ]
asmflags = [ "-DASSEMBLY" ]
if (is_debug) {
defines += [ "LK_DEBUGLEVEL=${debug_level}" ]
if (enable_debug_linebuffer) {
defines += [ "WITH_DEBUG_LINEBUFFER=1" ]
}
cflags += [ "-g" ]
if (target_cpu == "x64") {
cflags += [ "-gdwarf-2" ]
}
}
if (enable_vm) {
defines += [ "WITH_KERNEL_VM=1" ]
}
if (target_cpu == "x64") {
include_dirs += [ "//kernel/arch/x86/include" ]
cflags += [
"-mcmodel=kernel",
"-mno-red-zone",
]
if (!is_clang) {
cflags += [
"-mskip-rax-setup",
"-falign-jumps=1",
"-falign-loops=1",
"-falign-functions=4",
]
}
if (enable_smp) {
defines += [
"WITH_SMP=1",
"SMP_MAX_CPUS=16",
]
} else {
defines += [ "SMP_MAX_CPUS=1" ]
}
} else if (target_cpu == "arm") {
include_dirs += [
"//kernel/arch/arm/include",
"//kernel/arch/arm/arm/include",
]
if (enable_smp) {
defines += [
"WITH_SMP=1",
"SMP_MAX_CPUS=4",
"SMP_CPU_CLUSTER_SHIFT=8",
"SMP_CPU_ID_BITS=24",
]
} else {
defines += [ "WITH_SMP=0" ]
}
} else if (target_cpu == "arm64") {
include_dirs += [ "//kernel/arch/arm64/include" ]
if (enable_smp) {
defines += [
"WITH_SMP=1",
"SMP_MAX_CPUS=4",
"SMP_CPU_CLUSTER_SHIFT=8",
"SMP_CPU_ID_BITS=24",
]
} else {
defines += [ "WITH_SMP=0" ]
}
}
if (target_cpu == "x64") {
linker_script = "${root_gen_dir}/kernel.ld"
} else if (target_cpu == "arm") {
linker_script = "${root_gen_dir}/system-onesegment.ld"
} else if (target_cpu == "arm64") {
linker_script = "${root_gen_dir}/system-onesegment.ld"
}
ldflags = [
"-Wl,-T",
rebase_path(linker_script, root_out_dir),
]
}
config("user_defaults") {
defines = [
"_XOPEN_SOURCE=700",
"_BSD_SOURCE",
]
if (is_debug) {
cflags = [ "-g" ]
if (target_cpu == "x64") {
cflags += [ "-gdwarf-2" ]
}
}
if (target_cpu == "x64") {
linker_script = "//kernel/arch/x86/64/user.ld"
} else if (target_cpu == "arm") {
linker_script = "//kernel/arch/arm/user.ld"
} else if (target_cpu == "arm64") {
linker_script = "//kernel/arch/arm64/user.ld"
}
ldflags = [
"-Wl,-T",
rebase_path(linker_script, root_out_dir),
]
}
config("executable_ldconfig") {
ldflags = [
"-nostdlib",
"-Wl,-z,max-page-size=4096",
"-Wl,--gc-sections",
]
if (!is_clang) {
libs = [ exec_script("libgcc_path.sh", [ toolprefix ], "trim string") ]
}
}
config("default_warnings") {
# TODO(phosek): split these into multiple configs
cflags = [
"-Wall",
#-Wextra
#-Werror
"-Wno-multichar",
"-Wno-unused-parameter",
"-Wno-unused-function",
"-Wno-unused-label",
"-Werror=return-type",
#"-Wundef",
]
if (is_clang) {
cflags += [
"-Wno-incompatible-pointer-types-discards-qualifiers",
"-Wno-format",
]
}
}
config("strict_compiler_flags") {
cflags_c = [
"-Werror-implicit-function-declaration",
"-Wstrict-prototypes",
"-Wwrite-strings",
]
}
config("no_stack_protector") {
cflags = [ "-fno-stack-protector" ]
}
+96
Ver Arquivo
@@ -0,0 +1,96 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
if (target_os == "") {
target_os = "fuchsia"
}
if (target_cpu == "") {
target_cpu = host_cpu
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (current_os == "") {
current_os = target_os
}
declare_args() {
# The desired platform for the build.
target_platform = "qemu"
# Debug build.
is_debug = true
# Set to true when compiling with the Clang compiler.
is_clang = false
# This should not be set as a build argument.
target_toolchain = ""
# This should not be set as a build argument.
host_toolchain = ""
}
# All binary targets will get this list of configs by default.
_shared_binary_target_configs = [
"//gnbuild:compiler_defaults",
"//gnbuild:default_warnings",
"//gnbuild:strict_compiler_flags",
]
# Apply that default list to the binary target types.
set_defaults("source_set") {
configs = _shared_binary_target_configs
configs += [ "//gnbuild:user_defaults" ]
}
set_defaults("static_library") {
configs = _shared_binary_target_configs
configs += [ "//gnbuild:user_defaults" ]
}
set_defaults("module") {
configs = _shared_binary_target_configs
configs += [ "//gnbuild:kernel_defaults" ]
}
set_defaults("executable") {
configs = _shared_binary_target_configs
configs += [
"//gnbuild:user_defaults",
"//gnbuild:executable_ldconfig",
]
}
# Toolchain
if (target_cpu == "arm" || target_cpu == "arm64" || target_cpu == "x64") {
if (is_clang) {
target_toolchain = "//gnbuild/toolchain:clang_target"
} else {
target_toolchain = "//gnbuild/toolchain:target"
}
} else {
assert(false, "Target CPU not supported")
}
set_default_toolchain(target_toolchain)
if (host_os == "linux") {
host_toolchain = "//gnbuild/toolchain:gcc_host"
} else if (host_os == "mac") {
host_toolchain = "//gnbuild/toolchain:clang_host"
} else {
assert(false, "Host OS not supported")
}
# Module
# The template for kernel module.
#
# Accepts everything source_set accepts.
template("module") {
source_set(target_name) {
forward_variables_from(invoker, "*")
}
}
+164
Ver Arquivo
@@ -0,0 +1,164 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
# This file introduces two related templates that act like action and
# action_foreach but instead of running a script, it will compile a given tool
# in the host toolchain and run that (either once or over the list of inputs,
# depending on the variant).
#
# Parameters
#
# tool (required)
# [label] Label of the tool to run. This should be an executable, and
# this label should not include a toolchain (anything in parens). The
# host compile of this tool will be used.
#
# outputs (required)
# [list of files] Like the outputs of action (if using "compiled_action",
# this would be just the list of outputs), or action_foreach (if using
# "compiled_action_foreach", this would contain source expansions mapping
# input to output files).
#
# args (required)
# [list of strings] Same meaning as action/action_foreach.
#
# inputs (optional)
# Files the binary takes as input. The step will be re-run whenever any
# of these change. If inputs is empty, the step will run only when the
# binary itself changes.
#
# visibility
# deps
# args (all optional)
# Same meaning as action/action_foreach.
#
#
# Example of usage:
#
# compiled_action("run_my_tool") {
# tool = "//tools/something:mytool"
# outputs = [
# "$target_gen_dir/mysource.cc",
# "$target_gen_dir/mysource.h",
# ]
#
# # The tool takes this input.
# inputs = [ "my_input_file.idl" ]
#
# # In this case, the tool takes as arguments the input file and the output
# # build dir (both relative to the "cd" that the script will be run in)
# # and will produce the output files listed above.
# args = [
# rebase_path("my_input_file.idl", root_build_dir),
# "--output-dir", rebase_path(target_gen_dir, root_build_dir),
# ]
# }
#
# You would typically declare your tool like this:
# if (host_toolchain == current_toolchain) {
# executable("mytool") {
# ...
# }
# }
# The if statement around the executable is optional. That says "I only care
# about this target in the host toolchain". Usually this is what you want, and
# saves unnecessarily compiling your tool for the target platform. But if you
# need a target build of your tool as well, just leave off the if statement.
template("compiled_action") {
assert(defined(invoker.tool), "tool must be defined for $target_name")
assert(defined(invoker.outputs), "outputs must be defined for $target_name")
assert(defined(invoker.args), "args must be defined for $target_name")
assert(!defined(invoker.sources),
"compiled_action doesn't take a sources arg. Use inputs instead.")
action(target_name) {
forward_variables_from(invoker,
[
"deps",
"inputs",
"outputs",
"testonly",
"visibility",
])
if (!defined(deps)) {
deps = []
}
if (!defined(inputs)) {
inputs = []
}
script = "//gnbuild/gn_run_binary.sh"
# Constuct the host toolchain version of the tool.
host_tool = invoker.tool + "($host_toolchain)"
# Get the path to the executable. Currently, this assumes that the tool
# does not specify output_name so that the target name is the name to use.
# If that's not the case, we'll need another argument to the script to
# specify this, since we can't know what the output name is (it might be in
# another file not processed yet).
host_executable = get_label_info(host_tool, "root_out_dir") + "/" +
get_label_info(host_tool, "name")
# Add the executable itself as an input.
inputs += [ host_executable ]
deps += [ host_tool ]
# The script takes as arguments the binary to run, and then the arguments
# to pass it.
args = [ rebase_path(host_executable, root_build_dir) ] + invoker.args
}
}
template("compiled_action_foreach") {
assert(defined(invoker.sources), "sources must be defined for $target_name")
assert(defined(invoker.tool), "tool must be defined for $target_name")
assert(defined(invoker.outputs), "outputs must be defined for $target_name")
assert(defined(invoker.args), "args must be defined for $target_name")
action_foreach(target_name) {
forward_variables_from(invoker,
[
"deps",
"inputs",
"outputs",
"sources",
"testonly",
"visibility",
])
if (!defined(deps)) {
deps = []
}
if (!defined(inputs)) {
inputs = []
}
script = "//gnbuild/gn_run_binary.sh"
# Constuct the host toolchain version of the tool.
host_tool = invoker.tool + "($host_toolchain)"
# Get the path to the executable. Currently, this assumes that the tool
# does not specify output_name so that the target name is the name to use.
# If that's not the case, we'll need another argument to the script to
# specify this, since we can't know what the output name is (it might be in
# another file not processed yet).
host_executable = get_label_info(host_tool, "root_out_dir") + "/" +
get_label_info(host_tool, "name")
# Add the executable itself as an input.
inputs += [ host_executable ]
deps += [ host_tool ]
# The script takes as arguments the binary to run, and then the arguments
# to pass it.
args = [ rebase_path(host_executable, root_build_dir) ] + invoker.args
}
}
+131
Ver Arquivo
@@ -0,0 +1,131 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
declare_args() {
# Use linebuffer for debug output.
enable_debug_linebuffer = true
# Debug output verbosity.
debug_level = 2
# Predefines used by the kernel.
kernel_defines = []
}
# TODO(phosek): toolchain configuration could be moved to a separate file
if (is_clang) {
default_toolprefix = ""
} else if (target_cpu == "arm") {
default_toolprefix = "arm-eabi-"
} else if (target_cpu == "arm64") {
default_toolprefix = "aarch64-elf-"
} else if (target_cpu == "x64") {
default_toolprefix = "x86_64-elf-"
} else {
assert(false, "Target CPU not supported")
}
declare_args() {
# Toolchain prefix.
toolprefix = default_toolprefix
}
# Support for SMP.
enable_smp = false
# Support for virtual memory.
enable_vm = true
# Support for Intel HDA QEMU quirks.
enable_intel_hda_qemu_quirks = false
# Support for PCIe.
enable_pcie = true
# Support for application shell.
enable_shell = false
# Kernel console.
enable_console = true
# Support for debug log.
enable_debuglog = true
# Support for partitioning.
enable_partition = false
# Support for kernel log.
enable_klog = true
# The heap implementation used.
heap_implementation = "miniheap"
# Magenta.
enable_magenta = true
# Support for user boot.
enable_userboot = true
if (target_cpu == "x64") {
enable_vm = true
enable_smp = true
enable_debug_linebuffer = true
heap_implementation = "cmpctmalloc"
embed_user_bootfs = true
if (target_platform == "qemu") {
enable_intel_hda_qemu_quirks = true
}
# TODO(phosek): Turn these into a config in arch/x86
kernel_defines = [
"MEMBASE=0",
"KERNEL_ASPACE_BASE=0xffffff8000000000",
"KERNEL_ASPACE_SIZE=0x0000008000000000",
"KERNEL_BASE=0xffffffff80000000",
"KERNEL_LOAD_OFFSET=0x00100000",
"KERNEL_SIZE=0x40000000",
"USER_ASPACE_BASE=0x0000000001000000",
"USER_ASPACE_SIZE=0x00007fffff000000",
"HEADER_LOAD_OFFSET=0x00010000",
"PHYS_HEADER_LOAD_OFFSET=0x00002000",
]
} else if (target_cpu == "arm") {
enable_vm = true
enable_smp = true
enable_debug_linebuffer = true
heap_implementation = "cmpctmalloc"
embed_user_bootfs = true
# TODO(phosek): Turn these into a config in arch/arm
kernel_defines = [
"MEMBASE=0x40000000",
"MEMSIZE=0x08000000",
"KERNEL_ASPACE_BASE=0x40000000",
"KERNEL_ASPACE_SIZE=0xc0000000",
"KERNEL_BASE=0x80000000",
"KERNEL_LOAD_OFFSET=0x10000",
]
} else if (target_cpu == "arm64") {
enable_vm = true
enable_smp = true
enable_debug_linebuffer = true
heap_implementation = "cmpctmalloc"
embed_user_bootfs = true
# TODO(phosek): Turn these into a config in arch/arm64
kernel_defines = [
"MEMBASE=0x40000000",
"MEMSIZE=0x08000000",
"KERNEL_ASPACE_BASE=0xffff000000000000",
"KERNEL_ASPACE_SIZE=0x0001000000000000",
"USER_ASPACE_BASE=0x0000000001000000",
"USER_ASPACE_SIZE=0x0000fffffe000000",
"KERNEL_BASE=0xffff000000000000",
"KERNEL_LOAD_OFFSET=0x10000",
]
} else {
assert(false, "Target CPU not supported")
}
+46
Ver Arquivo
@@ -0,0 +1,46 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
import("//gnbuild/config.gni")
# Generate a raw binary file for a given file.
#
# Parameters
#
# input (required)
# Input file name
#
# output (required)
# Output file name
#
# deps (optional)
template("convert_to_raw") {
assert(defined(invoker.input), "must specify \"input\" value")
assert(defined(invoker.output), "must specify \"output\" value")
forward_variables_from(invoker,
[
"input",
"output",
])
action(target_name) {
forward_variables_from(invoker, [ "deps" ])
script = "//gnbuild/convert_to_raw.sh"
sources = [
input,
]
outputs = [
output,
]
args = [
toolprefix,
rebase_path(input),
rebase_path(output),
]
}
}
+15
Ver Arquivo
@@ -0,0 +1,15 @@
#!/bin/bash
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
# Generate a raw binary file from the given input file.
readonly PREFIX="$1"
readonly INPUT="$2"
readonly OUTPUT="$3"
"${PREFIX}objcopy" -O binary "${INPUT}" "${OUTPUT}"
+43
Ver Arquivo
@@ -0,0 +1,43 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
# Generate a linker script by replacing all variables with provided values.
#
# Parameters
#
# output (required)
# Input file name
#
# input (required)
# Output file name
#
# defines (required)
# List of defines used for substitution
template("generate_ld_script") {
assert(defined(invoker.input), "must specify \"input\" value")
assert(defined(invoker.output), "must specify \"output\" value")
assert(defined(invoker.defines), "must specify \"defines\" value")
forward_variables_from(invoker,
[
"input",
"output",
"defines",
])
action(target_name) {
script = "//gnbuild/generate_ld_script.sh"
sources = [
input,
]
outputs = [
output,
]
args = [
rebase_path(input),
rebase_path(output),
] + defines
}
}
+21
Ver Arquivo
@@ -0,0 +1,21 @@
#!/bin/bash
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
# Generate a linker script by substituting all variables with provided values.
declare PATTERN=""
for i in "$@"; do
case ${i} in
*=*)
PATTERN="${PATTERN}s/%${i%=*}%/${i#*=}/;"
;;
esac
done
sed "${PATTERN}" <$1 >$2
+11
Ver Arquivo
@@ -0,0 +1,11 @@
#!/bin/bash
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
# Helper script to run an arbitrary binary produced by the current build.
"./${1}" "${@:2}"
+11
Ver Arquivo
@@ -0,0 +1,11 @@
#!/bin/bash
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
TOOL_PREFIX=$1
${TOOL_PREFIX}gcc -print-libgcc-file-name
+291
Ver Arquivo
@@ -0,0 +1,291 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
import("//gnbuild/config.gni")
# Defines a toolchain which looks and behaves like gcc.
#
# Parameters
#
# cc
# cxx
# ar
# ld
#
# toolchain_cpu
# What "current_cpu" should be set to when invoking a build
# using this toolchain.
#
# toolchain_os
# What "current_os" should be set to when invoking a
# build using this toolchain.
#
# strip
# Whether to run strip on all shared libraries and executables as
# they're built.
#
# is_clang
# Whether to use clang instead of gcc.
#
# use_gold
# Whether the toolchain uses gold instead of ld.
template("gcc_toolchain") {
toolchain(target_name) {
assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value")
assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value")
assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value")
assert(defined(invoker.toolchain_cpu),
"gcc_toolchain() must specify a \"toolchain_cpu\"")
assert(defined(invoker.toolchain_os),
"gcc_toolchain() must specify a \"toolchain_os\"")
forward_variables_from(invoker,
[
"cc",
"cxx",
"ar",
"ld",
])
tool("cc") {
depfile = "{{output}}.d"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CC {{output}}"
outputs = [
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
]
}
tool("cxx") {
depfile = "{{output}}.d"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CXX {{output}}"
outputs = [
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
]
}
tool("asm") {
depfile = "{{output}}.d"
command = "$cc {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
description = "ASM {{output}}"
outputs = [
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
]
}
tool("alink") {
rspfile = "{{output}}.rsp"
command =
"rm -f {{output}} && $ar {{arflags}} rcsD {{output}} @\"$rspfile\""
description = "AR {{output}}"
rspfile_content = "{{inputs}}"
outputs = [
"{{output_dir}}/{{target_output_name}}{{output_extension}}",
]
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".a"
output_prefix = "lib"
}
tool("solink") {
outname = "{{target_output_name}}{{output_extension}}"
outfile = "{{output_dir}}/$outname"
rspfile = "$outfile.rsp"
unstripped_outfile = outfile
if (defined(invoker.strip)) {
unstripped_outfile = "{{root_out_dir}}/lib.unstripped/{{target_output_name}}{{output_extension}}"
}
if (invoker.toolchain_os == "mac") {
command = "$ld -shared {{ldflags}} -o \"$unstripped_outfile\" -Wl,-filelist,\"$rspfile\" {{libs}} {{solibs}}"
rspfile_content = "{{inputs_newline}}"
default_output_extension = ".dylib"
} else {
command = "$ld -shared {{ldflags}} -o \"$unstripped_outfile\" -Wl,-soname=\"$outname\" @\"$rspfile\""
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
default_output_extension = ".so"
}
if (defined(invoker.strip)) {
command +=
" && ${invoker.strip} -S -o \"$outfile\" \"$unstripped_outfile\""
}
description = "SOLINK $outfile"
default_output_dir = "{{root_out_dir}}"
output_prefix = "lib"
outputs = [
outfile,
]
if (outfile != unstripped_outfile) {
outputs += [ unstripped_outfile ]
}
}
tool("solink_module") {
outname = "{{target_output_name}}{{output_extension}}"
outfile = "{{output_dir}}/$outname"
rspfile = "$outfile.rsp"
if (defined(invoker.strip)) {
unstripped_outfile = "{{root_out_dir}}/lib.unstripped/{{target_output_name}}{{output_extension}}"
} else {
unstripped_outfile = outfile
}
if (invoker.toolchain_os == "mac") {
command = "$ld -shared {{ldflags}} -o \"$unstripped_outfile\" -Wl,-filelist,\"$rspfile\" {{libs}} {{solibs}}"
rspfile_content = "{{inputs_newline}}"
default_output_extension = ".dylib"
} else {
command = "$ld -shared {{ldflags}} -o \"$unstripped_outfile\" -Wl,-soname=\"$outname\" @\"$rspfile\""
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
default_output_extension = ".so"
}
if (defined(invoker.strip)) {
command +=
" && ${invoker.strip} -S -o \"$outfile\" \"$unstripped_outfile\""
}
description = "SOLINK $outfile"
default_output_dir = "{{root_out_dir}}"
output_prefix = "lib"
outputs = [
outfile,
]
if (outfile != unstripped_outfile) {
outputs += [ unstripped_outfile ]
}
}
tool("link") {
outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
rspfile = "$outfile.rsp"
unstripped_outfile = outfile
if (defined(invoker.strip)) {
unstripped_outfile = "{{root_out_dir}}/exe.unstripped/{{target_output_name}}{{output_extension}}"
}
if (invoker.toolchain_os == "mac") {
command = "$ld {{ldflags}} -o \"$unstripped_outfile\" -Wl,-filelist,\"$rspfile\" {{solibs}} {{libs}}"
} else {
command = "$ld {{ldflags}} -o \"$unstripped_outfile\" -Wl,--start-group @\"$rspfile\" {{solibs}} -Wl,--end-group {{libs}}"
}
if (defined(invoker.strip)) {
command +=
" && ${invoker.strip} -S -o \"$outfile\" \"$unstripped_outfile\""
}
description = "LINK $outfile"
rspfile_content = "{{inputs}}"
default_output_dir = "{{root_out_dir}}"
outputs = [
outfile,
]
if (outfile != unstripped_outfile) {
outputs += [ unstripped_outfile ]
}
}
tool("stamp") {
command = "touch {{output}}"
description = "STAMP {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
description = "COPY {{source}} {{output}}"
}
# When invoking this toolchain not as the default one, these args will be
# passed to the build. They are ignored when this is the default toolchain.
toolchain_args() {
current_cpu = invoker.toolchain_cpu
current_os = invoker.toolchain_os
# These values need to be passed through unchanged.
target_toolchain = target_toolchain
target_os = target_os
target_cpu = target_cpu
if (defined(invoker.is_clang)) {
is_clang = invoker.is_clang
}
if (defined(invoker.use_gold)) {
use_gold = invoker.use_gold
}
}
}
}
template("clang_toolchain") {
assert(defined(invoker.cc), "clang_toolchain() must specify a \"cc\" value")
assert(defined(invoker.cxx), "clang_toolchain() must specify a \"cxx\" value")
assert(defined(invoker.ar), "clang_toolchain() must specify a \"ar\" value")
assert(defined(invoker.ld), "clang_toolchain() must specify a \"ld\" value")
assert(defined(invoker.toolchain_cpu),
"clang_toolchain() must specify a \"toolchain_cpu\"")
assert(defined(invoker.toolchain_os),
"clang_toolchain() must specify a \"toolchain_os\"")
gcc_toolchain(target_name) {
is_clang = true
forward_variables_from(invoker,
[
"cc",
"cxx",
"ld",
"ar",
"strip",
"toolchain_cpu",
"toolchain_os",
"use_gold",
])
}
}
gcc_toolchain("target") {
cc = "${toolprefix}gcc"
cxx = "${toolprefix}g++"
ar = "${toolprefix}ar"
ld = cxx
strip = "${toolprefix}strip"
toolchain_cpu = target_cpu
toolchain_os = target_os
is_clang = false
}
clang_toolchain("clang_target") {
cc = "${toolprefix}clang"
cxx = "${toolprefix}clang++"
ld = cxx
ar = "${toolprefix}ar"
strip = "${toolprefix}strip"
toolchain_cpu = target_cpu
toolchain_os = target_os
}
gcc_toolchain("gcc_host") {
cc = "gcc"
cxx = "g++"
ar = "ar"
ld = cxx
strip = "strip"
toolchain_cpu = host_cpu
toolchain_os = host_os
is_clang = false
}
clang_toolchain("clang_host") {
cc = "clang"
cxx = "clang++"
ld = cxx
ar = "ar"
strip = "strip"
toolchain_cpu = host_cpu
toolchain_os = host_os
}
+21
Ver Arquivo
@@ -0,0 +1,21 @@
Copyright 2016 The Fuchsia Authors
Copyright (c) 2008-2015 Travis Geiselbrecht
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+14
Ver Arquivo
@@ -0,0 +1,14 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
module("app") {
sources = [
"app.c",
]
deps = [
"//kernel/lib/libc",
]
}
+55
Ver Arquivo
@@ -0,0 +1,55 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdio.h>
#include <app.h>
#include <compiler.h>
#include <kernel/thread.h>
extern const struct app_descriptor __start_apps[] __WEAK;
extern const struct app_descriptor __stop_apps[] __WEAK;
static void start_app(const struct app_descriptor *app);
/* one time setup */
void apps_init(void)
{
const struct app_descriptor *app;
/* call all the init routines */
for (app = __start_apps; app != __stop_apps; app++) {
if (app->init)
app->init(app);
}
/* start any that want to start on boot */
for (app = __start_apps; app != __stop_apps; app++) {
if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {
start_app(app);
}
}
}
static int app_thread_entry(void *arg)
{
const struct app_descriptor *app = (const struct app_descriptor *)arg;
app->entry(app, NULL);
return 0;
}
static void start_app(const struct app_descriptor *app)
{
uint32_t stack_size = (app->flags & APP_FLAG_CUSTOM_STACK_SIZE) ? app->stack_size : DEFAULT_STACK_SIZE;
printf("starting app %s\n", app->name);
thread_t *t = thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, stack_size);
thread_detach(t);
thread_resume(t);
}
+18
Ver Arquivo
@@ -0,0 +1,18 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
module("inetsrv") {
sources = [
"inetsrv.c",
"inetsrv.h",
]
deps = [
"//kernel/lib/libc",
"//kernel/lib/minip",
"//kernel/lib/tftp",
"//third_party/lib/cksum",
]
}
+223
Ver Arquivo
@@ -0,0 +1,223 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app.h>
#include <err.h>
#include <debug.h>
#include <trace.h>
#include <stdio.h>
#include <stdlib.h>
#include <compiler.h>
#include <kernel/thread.h>
#include <lib/minip.h>
#include <lib/tftp.h>
#include <lib/cksum.h>
#include <platform.h>
#include "inetsrv.h"
static int chargen_worker(void *socket)
{
uint64_t count = 0;
tcp_socket_t *s = socket;
/* enough buffer to hold an entire defacto chargen sequences */
#define CHARGEN_BUFSIZE (0x5f * 0x5f) // 9025 bytes
uint8_t *buf = malloc(CHARGEN_BUFSIZE);
if (!buf)
return ERR_NO_MEMORY;
/* generate the sequence */
uint8_t c = '!';
for (size_t i = 0; i < CHARGEN_BUFSIZE; i++) {
buf[i] = c++;
if (c == 0x7f)
c = ' ';
}
lk_time_t t = current_time();
for (;;) {
ssize_t ret = tcp_write(s, buf, CHARGEN_BUFSIZE);
//TRACEF("tcp_write returns %d\n", ret);
if (ret < 0)
break;
count += ret;
}
t = current_time() - t;
TRACEF("chargen worker exiting, wrote %llu bytes in %u msecs (%llu bytes/sec)\n",
count, (uint32_t)t, count * 1000 / t);
free(buf);
tcp_close(s);
return 0;
}
static int chargen_server(void *arg)
{
status_t err;
tcp_socket_t *listen_socket;
err = tcp_open_listen(&listen_socket, 19);
if (err < 0) {
TRACEF("error opening chargen listen socket\n");
return -1;
}
for (;;) {
tcp_socket_t *accept_socket;
err = tcp_accept(listen_socket, &accept_socket);
TRACEF("tcp_accept returns returns %d, handle %p\n", err, accept_socket);
if (err < 0) {
TRACEF("error accepting socket, retrying\n");
continue;
}
TRACEF("starting chargen worker\n");
thread_detach_and_resume(thread_create("chargen_worker", &chargen_worker, accept_socket, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
}
static int discard_worker(void *socket)
{
uint64_t count = 0;
uint32_t crc = 0;
tcp_socket_t *s = socket;
#define DISCARD_BUFSIZE 1024
uint8_t *buf = malloc(DISCARD_BUFSIZE);
if (!buf) {
TRACEF("error allocating buffer\n");
}
lk_time_t t = current_time();
for (;;) {
ssize_t ret = tcp_read(s, buf, DISCARD_BUFSIZE);
if (ret <= 0)
break;
crc = crc32(crc, buf, ret);
count += ret;
}
t = current_time() - t;
TRACEF("discard worker exiting, read %llu bytes in %u msecs (%llu bytes/sec), crc32 0x%x\n",
count, (uint32_t)t, count * 1000 / t, crc);
tcp_close(s);
free(buf);
return 0;
}
static int discard_server(void *arg)
{
status_t err;
tcp_socket_t *listen_socket;
err = tcp_open_listen(&listen_socket, 9);
if (err < 0) {
TRACEF("error opening discard listen socket\n");
return -1;
}
for (;;) {
tcp_socket_t *accept_socket;
err = tcp_accept(listen_socket, &accept_socket);
TRACEF("tcp_accept returns returns %d, handle %p\n", err, accept_socket);
if (err < 0) {
TRACEF("error accepting socket, retrying\n");
continue;
}
TRACEF("starting discard worker\n");
thread_detach_and_resume(thread_create("discard_worker", &discard_worker, accept_socket, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
}
static int echo_worker(void *socket)
{
tcp_socket_t *s = socket;
#define ECHO_BUFSIZE 1024
uint8_t *buf = malloc(ECHO_BUFSIZE);
if (!buf) {
TRACEF("error allocating buffer\n");
return ERR_NO_MEMORY;
}
for (;;) {
ssize_t ret = tcp_read(s, buf, sizeof(buf));
if (ret <= 0)
break;
tcp_write(s, buf, ret);
if (ret <= 0)
break;
}
TRACEF("echo worker exiting\n");
tcp_close(s);
free(buf);
return 0;
}
static int echo_server(void *arg)
{
status_t err;
tcp_socket_t *listen_socket;
err = tcp_open_listen(&listen_socket, 7);
if (err < 0) {
TRACEF("error opening echo listen socket\n");
return -1;
}
for (;;) {
tcp_socket_t *accept_socket;
err = tcp_accept(listen_socket, &accept_socket);
TRACEF("tcp_accept returns returns %d, handle %p\n", err, accept_socket);
if (err < 0) {
TRACEF("error accepting socket, retrying\n");
continue;
}
TRACEF("starting echo worker\n");
thread_detach_and_resume(thread_create("echo_worker", &echo_worker, accept_socket, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
}
static void inetsrv_init(const struct app_descriptor *app)
{
}
static void inetsrv_entry(const struct app_descriptor *app, void *args)
{
/* XXX wait for the stack to initialize */
printf("starting internet servers\n");
thread_detach_and_resume(thread_create("chargen", &chargen_server, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("discard", &discard_server, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("echo", &echo_server, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
tftp_server_init(NULL);
}
APP_START(inetsrv)
.init = inetsrv_init,
.entry = inetsrv_entry,
.flags = 0,
APP_END
+9
Ver Arquivo
@@ -0,0 +1,9 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
+21
Ver Arquivo
@@ -0,0 +1,21 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/inetsrv.c \
MODULE_DEPS := \
lib/cksum \
lib/minip \
lib/tftp \
include make/module.mk
+11
Ver Arquivo
@@ -0,0 +1,11 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
module("pci_tests") {
sources = [
"pci_tests.c",
]
}
+245
Ver Arquivo
@@ -0,0 +1,245 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Corey Tabaka
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app.h>
#include <debug.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <compiler.h>
#include <platform.h>
#include <dev/pci.h>
#if defined(WITH_LIB_CONSOLE)
#include <lib/console.h>
/*
* enumerates pci devices
*/
static void pci_list(void)
{
pci_location_t state;
uint16_t device_id, vendor_id;
uint8_t header_type;
uint8_t base_class, sub_class, interface;
int busses = 0, devices = 0, lines = 0, devfn, ret;
int c;
printf("Scanning...\n");
for (state.bus = 0; state.bus <= pci_get_last_bus(); state.bus++) {
busses++;
for (devfn = 0; devfn < 256; devfn++) {
state.dev_fn = devfn;
ret = pci_read_config_half(&state, PCI_CONFIG_VENDOR_ID, &vendor_id);
if (ret != _PCI_SUCCESSFUL) goto error;
ret = pci_read_config_half(&state, PCI_CONFIG_DEVICE_ID, &device_id);
if (ret != _PCI_SUCCESSFUL) goto error;
ret = pci_read_config_byte(&state, PCI_CONFIG_HEADER_TYPE, &header_type);
if (ret != _PCI_SUCCESSFUL) goto error;
ret = pci_read_config_byte(&state, PCI_CONFIG_CLASS_CODE_BASE, &base_class);
if (ret != _PCI_SUCCESSFUL) goto error;
ret = pci_read_config_byte(&state, PCI_CONFIG_CLASS_CODE_SUB, &sub_class);
if (ret != _PCI_SUCCESSFUL) goto error;
ret = pci_read_config_byte(&state, PCI_CONFIG_CLASS_CODE_INTR, &interface);
if (ret != _PCI_SUCCESSFUL) goto error;
if (vendor_id != 0xffff) {
printf("%02x:%02x vendor_id=%04x device_id=%04x, header_type=%02x "
"base_class=%02x, sub_class=%02x, interface=%02x\n", state.bus, state.dev_fn,
vendor_id, device_id, header_type, base_class, sub_class, interface);
devices++;
lines++;
}
if (~header_type & PCI_HEADER_TYPE_MULTI_FN) {
// this is not a multi-function device, so advance to the next device
devfn |= 7;
}
if (lines == 23) {
printf("... press any key to continue, q to quit ...");
while ((c = getchar()) < 0);
printf("\n");
lines = 0;
if (c == 'q' || c == 'Q') goto quit;
}
}
}
printf("... done. Scanned %d busses, %d device/functions\n", busses, devices);
quit:
return;
error:
printf("Error while reading PCI config space: %02x\n", ret);
}
/*
* a somewhat fugly pci config space examine/modify command. this should probably
* be broken up a bit.
*/
static int pci_config(int argc, const cmd_args *argv)
{
pci_location_t loc;
pci_config_t config;
uint32_t offset;
unsigned int i;
int ret;
if (argc < 5) {
return -1;
}
if (!strcmp(argv[2].str, "dump")) {
loc.bus = atoui(argv[3].str);
loc.dev_fn = atoui(argv[4].str);
for (i=0; i < sizeof(pci_config_t); i++) {
ret = pci_read_config_byte(&loc, i, (uint8_t *) &config + i);
if (ret != _PCI_SUCCESSFUL) goto error;
}
printf("Device at %02x:%02x vendor id=%04x device id=%04x\n", loc.bus,
loc.dev_fn, config.vendor_id, config.device_id);
printf("command=%04x status=%04x pi=%02x sub cls=%02x base cls=%02x\n",
config.command, config.status, config.program_interface,
config.sub_class, config.base_class);
for (i=0; i < 6; i+=2) {
printf("bar%d=%08x bar%d=%08x\n", i, config.base_addresses[i],
i+1, config.base_addresses[i+1]);
}
} else if (!strcmp(argv[2].str, "rb") || !strcmp(argv[2].str, "rh") || !strcmp(argv[2].str, "rw")) {
if (argc != 6) {
return -1;
}
loc.bus = atoui(argv[3].str);
loc.dev_fn = atoui(argv[4].str);
offset = atoui(argv[5].str);
switch (argv[2].str[1]) {
case 'b': {
uint8_t value;
ret = pci_read_config_byte(&loc, offset, &value);
if (ret != _PCI_SUCCESSFUL) goto error;
printf("byte at device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
}
break;
case 'h': {
uint16_t value;
ret = pci_read_config_half(&loc, offset, &value);
if (ret != _PCI_SUCCESSFUL) goto error;
printf("half at device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
}
break;
case 'w': {
uint32_t value;
ret = pci_read_config_word(&loc, offset, &value);
if (ret != _PCI_SUCCESSFUL) goto error;
printf("word at device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
}
break;
}
} else if (!strcmp(argv[2].str, "mb") || !strcmp(argv[2].str, "mh") || !strcmp(argv[2].str, "mw")) {
if (argc != 7) {
return -1;
}
loc.bus = atoui(argv[3].str);
loc.dev_fn = atoui(argv[4].str);
offset = atoui(argv[5].str);
switch (argv[2].str[1]) {
case 'b': {
uint8_t value = atoui(argv[6].str);
ret = pci_write_config_byte(&loc, offset, value);
if (ret != _PCI_SUCCESSFUL) goto error;
printf("byte to device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
}
break;
case 'h': {
uint16_t value = atoui(argv[6].str);
ret = pci_write_config_half(&loc, offset, value);
if (ret != _PCI_SUCCESSFUL) goto error;
printf("half to device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
}
break;
case 'w': {
uint32_t value = atoui(argv[6].str);
ret = pci_write_config_word(&loc, offset, value);
if (ret != _PCI_SUCCESSFUL) goto error;
printf("word to device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
}
break;
}
} else {
return -1;
}
return 0;
error:
printf("Error while reading PCI config space: %02x\n", ret);
return -2;
}
static int pci_cmd(int argc, const cmd_args *argv)
{
if (argc < 2) {
printf("pci commands:\n");
usage:
printf("%s list\n", argv[0].str);
printf("%s config dump <bus> <devfn>\n", argv[0].str);
printf("%s config <rb|rh|rw> <bus> <devfn> <offset>\n", argv[0].str);
printf("%s config <mb|mh|mw> <bus> <devfn> <offset> <value>\n", argv[0].str);
goto out;
}
if (!strcmp(argv[1].str, "list")) {
pci_list();
} else if (!strcmp(argv[1].str, "config")) {
if (pci_config(argc, argv)) {
goto usage;
}
} else {
goto usage;
}
out:
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("pci", "pci toolbox", &pci_cmd)
STATIC_COMMAND_END(pcitests);
#endif
APP_START(pcitests)
APP_END
+15
Ver Arquivo
@@ -0,0 +1,15 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/pci_tests.c
include make/module.mk
+15
Ver Arquivo
@@ -0,0 +1,15 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/app.c
include make/module.mk
+31
Ver Arquivo
@@ -0,0 +1,31 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
import("//gnbuild/config.gni")
config("enable_shell") {
if (enable_shell) {
defines = [ "WITH_APP_SHELL=1" ]
} else {
defines = [ "WITH_APP_SHELL=0" ]
}
}
config("_shell_config") {
visibility = [ ":*" ]
configs = [ ":enable_shell" ]
}
module("shell") {
public_configs = [ ":_shell_config" ]
sources = [
"shell.c",
]
deps = [
"//kernel/lib/console",
"//kernel/lib/libc",
]
}
+18
Ver Arquivo
@@ -0,0 +1,18 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_DEPS += \
lib/console
MODULE_SRCS += \
$(LOCAL_DIR)/shell.c
include make/module.mk
+26
Ver Arquivo
@@ -0,0 +1,26 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app.h>
#include <debug.h>
#include <lib/console.h>
static void shell_init(const struct app_descriptor *app)
{
console_init();
}
static void shell_entry(const struct app_descriptor *app, void *args)
{
console_start();
}
APP_START(shell)
.init = shell_init,
.entry = shell_entry,
APP_END
+15
Ver Arquivo
@@ -0,0 +1,15 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
module("stringtests") {
sources = [
"string_tests.c",
]
deps = [
"//kernel/lib/console",
"//kernel/lib/libc",
]
}
+17
Ver Arquivo
@@ -0,0 +1,17 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/string_tests.c \
# put arch local .S files here if developing memcpy/memmove
include make/module.mk
+345
Ver Arquivo
@@ -0,0 +1,345 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <app.h>
#include <platform.h>
#include <kernel/thread.h>
static uint8_t *src;
static uint8_t *dst;
static uint8_t *src2;
static uint8_t *dst2;
#define BUFFER_SIZE (2*1024*1024)
#define ITERATIONS (256*1024*1024 / BUFFER_SIZE) // enough iterations to have to copy/set 256MB of memory
#if 1
static inline void *mymemcpy(void *dst, const void *src, size_t len) { return memcpy(dst, src, len); }
static inline void *mymemset(void *dst, int c, size_t len) { return memset(dst, c, len); }
#else
// if we're testing our own memcpy, use this
extern void *mymemcpy(void *dst, const void *src, size_t len);
extern void *mymemset(void *dst, int c, size_t len);
#endif
/* reference implementations of memmove/memcpy */
typedef long word;
#define lsize sizeof(word)
#define lmask (lsize - 1)
static void *c_memmove(void *dest, void const *src, size_t count)
{
char *d = (char *)dest;
const char *s = (const char *)src;
int len;
if (count == 0 || dest == src)
return dest;
if ((long)d < (long)s) {
if (((long)d | (long)s) & lmask) {
// src and/or dest do not align on word boundary
if ((((long)d ^ (long)s) & lmask) || (count < lsize))
len = count; // copy the rest of the buffer with the byte mover
else
len = lsize - ((long)d & lmask); // move the ptrs up to a word boundary
count -= len;
for (; len > 0; len--)
*d++ = *s++;
}
for (len = count / lsize; len > 0; len--) {
*(word *)d = *(word *)s;
d += lsize;
s += lsize;
}
for (len = count & lmask; len > 0; len--)
*d++ = *s++;
} else {
d += count;
s += count;
if (((long)d | (long)s) & lmask) {
// src and/or dest do not align on word boundary
if ((((long)d ^ (long)s) & lmask) || (count <= lsize))
len = count;
else
len = ((long)d & lmask);
count -= len;
for (; len > 0; len--)
*--d = *--s;
}
for (len = count / lsize; len > 0; len--) {
d -= lsize;
s -= lsize;
*(word *)d = *(word *)s;
}
for (len = count & lmask; len > 0; len--)
*--d = *--s;
}
return dest;
}
static void *c_memset(void *s, int c, size_t count)
{
char *xs = (char *) s;
size_t len = (-(size_t)s) & lmask;
word cc = c & 0xff;
if ( count > len ) {
count -= len;
cc |= cc << 8;
cc |= cc << 16;
if (sizeof(word) == 8)
cc |= (uint64_t)cc << 32; // should be optimized out on 32 bit machines
// write to non-aligned memory byte-wise
for ( ; len > 0; len-- )
*xs++ = c;
// write to aligned memory dword-wise
for ( len = count / lsize; len > 0; len-- ) {
*((word *)xs) = (word)cc;
xs += lsize;
}
count &= lmask;
}
// write remaining bytes
for ( ; count > 0; count-- )
*xs++ = c;
return s;
}
static void *null_memcpy(void *dst, const void *src, size_t len)
{
return dst;
}
static lk_time_t bench_memcpy_routine(void *memcpy_routine(void *, const void *, size_t), size_t srcalign, size_t dstalign)
{
int i;
lk_time_t t0;
t0 = current_time();
for (i=0; i < ITERATIONS; i++) {
memcpy_routine(dst + dstalign, src + srcalign, BUFFER_SIZE);
}
return current_time() - t0;
}
static void bench_memcpy(void)
{
lk_time_t null, c, libc, mine;
size_t srcalign, dstalign;
printf("memcpy speed test\n");
thread_sleep(200); // let the debug string clear the serial port
for (srcalign = 0; srcalign < 64; ) {
for (dstalign = 0; dstalign < 64; ) {
null = bench_memcpy_routine(&null_memcpy, srcalign, dstalign);
c = bench_memcpy_routine(&c_memmove, srcalign, dstalign);
libc = bench_memcpy_routine(&memcpy, srcalign, dstalign);
mine = bench_memcpy_routine(&mymemcpy, srcalign, dstalign);
printf("srcalign %zu, dstalign %zu: ", srcalign, dstalign);
printf(" null memcpy %u msecs\n", null);
printf("c memcpy %u msecs, %llu bytes/sec; ", c, (uint64_t)BUFFER_SIZE * ITERATIONS * 1000ULL / c);
printf("libc memcpy %u msecs, %llu bytes/sec; ", libc, (uint64_t)BUFFER_SIZE * ITERATIONS * 1000ULL / libc);
printf("my memcpy %u msecs, %llu bytes/sec; ", mine, (uint64_t)BUFFER_SIZE * ITERATIONS * 1000ULL / mine);
printf("\n");
if (dstalign < 8)
dstalign++;
else
dstalign <<= 1;
}
if (srcalign < 8)
srcalign++;
else
srcalign <<= 1;
}
}
static void fillbuf(void *ptr, size_t len, uint32_t seed)
{
size_t i;
for (i = 0; i < len; i++) {
((char *)ptr)[i] = seed;
seed *= 0x1234567;
}
}
static void validate_memcpy(void)
{
size_t srcalign, dstalign, size;
const size_t maxsize = 256;
printf("testing memcpy for correctness\n");
/*
* do the simple tests to make sure that memcpy doesn't color outside
* the lines for all alignment cases
*/
for (srcalign = 0; srcalign < 64; srcalign++) {
printf("srcalign %zu\n", srcalign);
for (dstalign = 0; dstalign < 64; dstalign++) {
//printf("\tdstalign %zu\n", dstalign);
for (size = 0; size < maxsize; size++) {
//printf("srcalign %zu, dstalign %zu, size %zu\n", srcalign, dstalign, size);
fillbuf(src, maxsize * 2, 567);
fillbuf(src2, maxsize * 2, 567);
fillbuf(dst, maxsize * 2, 123514);
fillbuf(dst2, maxsize * 2, 123514);
c_memmove(dst + dstalign, src + srcalign, size);
memcpy(dst2 + dstalign, src2 + srcalign, size);
int comp = memcmp(dst, dst2, maxsize * 2);
if (comp != 0) {
printf("error! srcalign %zu, dstalign %zu, size %zu\n", srcalign, dstalign, size);
}
}
}
}
}
static lk_time_t bench_memset_routine(void *memset_routine(void *, int, size_t), size_t dstalign, size_t len)
{
int i;
lk_time_t t0;
t0 = current_time();
for (i=0; i < ITERATIONS; i++) {
memset_routine(dst + dstalign, 0, len);
}
return current_time() - t0;
}
static void bench_memset(void)
{
lk_time_t c, libc, mine;
size_t dstalign;
printf("memset speed test\n");
thread_sleep(200); // let the debug string clear the serial port
for (dstalign = 0; dstalign < 64; dstalign++) {
c = bench_memset_routine(&c_memset, dstalign, BUFFER_SIZE);
libc = bench_memset_routine(&memset, dstalign, BUFFER_SIZE);
mine = bench_memset_routine(&mymemset, dstalign, BUFFER_SIZE);
printf("dstalign %zu: ", dstalign);
printf("c memset %u msecs, %llu bytes/sec; ", c, (uint64_t)BUFFER_SIZE * ITERATIONS * 1000ULL / c);
printf("libc memset %u msecs, %llu bytes/sec; ", libc, (uint64_t)BUFFER_SIZE * ITERATIONS * 1000ULL / libc);
printf("my memset %u msecs, %llu bytes/sec; ", mine, (uint64_t)BUFFER_SIZE * ITERATIONS * 1000ULL / mine);
printf("\n");
}
}
static void validate_memset(void)
{
size_t dstalign, size;
int c;
const size_t maxsize = 256;
printf("testing memset for correctness\n");
for (dstalign = 0; dstalign < 64; dstalign++) {
printf("align %zd\n", dstalign);
for (size = 0; size < maxsize; size++) {
for (c = -1; c < 257; c++) {
fillbuf(dst, maxsize * 2, 123514);
fillbuf(dst2, maxsize * 2, 123514);
c_memset(dst + dstalign, c, size);
memset(dst2 + dstalign, c, size);
int comp = memcmp(dst, dst2, maxsize * 2);
if (comp != 0) {
printf("error! align %zu, c 0x%hhx, size %zu\n", dstalign, c, size);
}
}
}
}
}
#if defined(WITH_LIB_CONSOLE)
#include <lib/console.h>
static int string_tests(int argc, const cmd_args *argv)
{
src = memalign(64, BUFFER_SIZE + 256);
dst = memalign(64, BUFFER_SIZE + 256);
src2 = memalign(64, BUFFER_SIZE + 256);
dst2 = memalign(64, BUFFER_SIZE + 256);
printf("src %p, dst %p\n", src, dst);
printf("src2 %p, dst2 %p\n", src2, dst2);
if (!src || !dst || !src2 || !dst2) {
printf("failed to allocate all the buffers\n");
goto out;
}
if (argc < 3) {
printf("not enough arguments:\n");
usage:
printf("%s validate <routine>\n", argv[0].str);
printf("%s bench <routine>\n", argv[0].str);
goto out;
}
if (!strcmp(argv[1].str, "validate")) {
if (!strcmp(argv[2].str, "memcpy")) {
validate_memcpy();
} else if (!strcmp(argv[2].str, "memset")) {
validate_memset();
}
} else if (!strcmp(argv[1].str, "bench")) {
if (!strcmp(argv[2].str, "memcpy")) {
bench_memcpy();
} else if (!strcmp(argv[2].str, "memset")) {
bench_memset();
}
} else {
goto usage;
}
out:
free(src);
free(dst);
free(src2);
free(dst2);
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("string", "memcpy tests", &string_tests)
STATIC_COMMAND_END(stringtests);
#endif
APP_START(stringtests)
APP_END
+52
Ver Arquivo
@@ -0,0 +1,52 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
module("tests") {
configs += [ "//kernel/kernel:enable_vm" ]
cflags = [
"-Wno-format",
"-fno-builtin",
]
include_dirs = [ "include" ]
public = [
"include/app/tests.h",
]
sources = [
"arena_tests.cpp",
"auto_call_tests.cpp",
"benchmarks.c",
"cache_tests.c",
"clock_tests.c",
"fibo.c",
"float.c",
"float_instructions.S",
"float_test_vec.c",
"forward_tests.cpp",
"hash_tests.cpp",
"list_tests.cpp",
"mem_tests.c",
"port_tests.c",
"printf_tests.c",
"ref_call_counter.cpp",
"ref_call_counter.h",
"ref_counted_tests.cpp",
"ref_ptr_tests.cpp",
"sync_ipi_tests.c",
"tests.c",
"thread_tests.c",
"unique_ptr_tests.cpp",
"unittests.c",
"vm_tests.cpp",
]
deps = [
"//kernel/lib/console",
"//kernel/lib/crypto",
"//kernel/lib/libc",
"//kernel/lib/unittest",
"//kernel/lib/utils",
"//third_party/lib/safeint",
]
}
+68
Ver Arquivo
@@ -0,0 +1,68 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <unittest.h>
#include <utils/arena.h>
static int arena_dtor_count = 0;
struct ArenaFoo {
char ff;
int xx, yy, zz;
ArenaFoo(int x, int y, int z) : ff(0), xx(x), yy(y), zz(z) {}
~ArenaFoo() { ++arena_dtor_count; }
};
extern "C" int arena_tests(int argc, const cmd_args* argv)
{
bool all_ok = true;
utils::TypedArena<ArenaFoo> arena;
arena.Init("arena_tests", 1000);
const int count = 30;
for (int times = 0; times != 5; ++times) {
ArenaFoo* afp[count] = {0};
for (int ix = 0; ix != count; ++ix) {
afp[ix] = arena.New(17, 5, ix + 100);
EXPECT_TRUE(afp[ix] != nullptr, "");
}
arena.Delete(afp[3]);
arena.Delete(afp[4]);
arena.Delete(afp[5]);
afp[3] = afp[4] = afp[5] = nullptr;
afp[4] = arena.New(17, 5, 104);
for (int ix = 0; ix != count; ++ix) {
if (!afp[ix]) continue;
EXPECT_EQ(17, afp[ix]->xx, "");
EXPECT_EQ(5, afp[ix]->yy, "");
EXPECT_EQ(ix + 100, afp[ix]->zz, "");
arena.Delete(afp[ix]);
}
EXPECT_EQ((count + 1) * (times + 1), arena_dtor_count, "");
// Leak a few objects.
for (int ix = 0; ix != 7; ++ix) {
auto leak = arena.New(2121, 77, 55);
EXPECT_TRUE(leak != nullptr, "");
}
}
printf("arena tests : %s\n", all_ok ? "ok" : "failed");
return 0;
}
+85
Ver Arquivo
@@ -0,0 +1,85 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <utils/auto_call.h>
#include <app/tests.h>
#include <unittest.h>
static volatile int test_func_count;
__NO_INLINE static void test_func()
{
test_func_count++;
}
int auto_call_tests(int argc, const cmd_args *argv)
{
// extern int foo();
// int a;
//
// auto ac = MakeAutoCall([&](){ a = 1; });
// auto ac2 = MakeAutoCall(foo);
//
// auto func = [&](){ a = 2; };
// AutoCall<decltype(bleh)> ac3(func);
// AutoCall<decltype(&foo)> ac4(&foo);
//
// // abort the call
// ac2.cancel();
BEGIN_TEST;
// mark as volatile to make sure it generates code
volatile int a;
// call a lambda
{
a = 0;
auto ac = utils::MakeAutoCall([&](){ a++; });
EXPECT_EQ(a, 0, "autocall hasn't run");
}
EXPECT_EQ(a, 1, "autocall has run");
{
a = 0;
auto ac = utils::MakeAutoCall([&](){ a++; });
EXPECT_EQ(a, 0, "autocall hasn't run");
ac.cancel();
EXPECT_EQ(a, 0, "autocall still hasn't run");
ac.call();
EXPECT_EQ(a, 0, "autocall still hasn't run");
}
EXPECT_EQ(a, 0, "autocall still hasn't run");
{
a = 0;
auto ac = utils::MakeAutoCall([&](){ a++; });
EXPECT_EQ(a, 0, "autocall hasn't run");
ac.call();
EXPECT_EQ(a, 1, "autocall should have run\n");
ac.cancel();
EXPECT_EQ(a, 1, "autocall ran only once\n");
}
EXPECT_EQ(a, 1, "autocall ran only once\n");
// call a regular function
{
test_func_count = 0;
auto ac = utils::MakeAutoCall(&test_func);
EXPECT_EQ(test_func_count, 0, "autocall hasn't run");
}
EXPECT_EQ(test_func_count, 1, "autocall has run");
END_TEST;
return 0;
}
+240
Ver Arquivo
@@ -0,0 +1,240 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <sys/types.h>
#include <stdio.h>
#include <rand.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <app/tests.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include <kernel/semaphore.h>
#include <kernel/event.h>
#include <platform.h>
const size_t BUFSIZE = (1024*1024);
const uint ITER = 1024;
__NO_INLINE static void bench_set_overhead(void)
{
uint32_t *buf = malloc(BUFSIZE);
uint count = arch_cycle_count();
for (uint i = 0; i < ITER; i++) {
__asm__ volatile("");
}
count = arch_cycle_count() - count;
printf("took %u cycles overhead to loop %u times\n",
count, ITER);
free(buf);
}
__NO_INLINE static void bench_memset(void)
{
void *buf = malloc(BUFSIZE);
uint count = arch_cycle_count();
for (uint i = 0; i < ITER; i++) {
memset(buf, 0, BUFSIZE);
}
count = arch_cycle_count() - count;
uint64_t bytes_cycle = (BUFSIZE * ITER * 1000ULL) / count;
printf("took %u cycles to memset a buffer of size %u %d times (%u bytes), %llu.%03llu bytes/cycle\n",
count, BUFSIZE, ITER, BUFSIZE * ITER, bytes_cycle / 1000, bytes_cycle % 1000);
free(buf);
}
#define bench_cset(type) \
__NO_INLINE static void bench_cset_##type(void) \
{ \
type *buf = malloc(BUFSIZE); \
\
uint count = arch_cycle_count(); \
for (uint i = 0; i < ITER; i++) { \
for (uint j = 0; j < BUFSIZE / sizeof(*buf); j++) { \
buf[j] = 0; \
} \
} \
count = arch_cycle_count() - count; \
\
uint64_t bytes_cycle = (BUFSIZE * ITER * 1000ULL) / count; \
printf("took %u cycles to manually clear a buffer using wordsize %d of size %u %d times (%u bytes), %llu.%03llu bytes/cycle\n", \
count, sizeof(*buf), BUFSIZE, ITER, BUFSIZE * ITER, bytes_cycle / 1000, bytes_cycle % 1000); \
\
free(buf); \
}
bench_cset(uint8_t)
bench_cset(uint16_t)
bench_cset(uint32_t)
bench_cset(uint64_t)
__NO_INLINE static void bench_cset_wide(void)
{
uint32_t *buf = malloc(BUFSIZE);
uint count = arch_cycle_count();
for (uint i = 0; i < ITER; i++) {
for (uint j = 0; j < BUFSIZE / sizeof(*buf) / 8; j++) {
buf[j*8] = 0;
buf[j*8+1] = 0;
buf[j*8+2] = 0;
buf[j*8+3] = 0;
buf[j*8+4] = 0;
buf[j*8+5] = 0;
buf[j*8+6] = 0;
buf[j*8+7] = 0;
}
}
count = arch_cycle_count() - count;
uint64_t bytes_cycle = (BUFSIZE * ITER * 1000ULL) / count;
printf("took %u cycles to manually clear a buffer of size %u %d times 8 words at a time (%u bytes), %llu.%03llu bytes/cycle\n",
count, BUFSIZE, ITER, BUFSIZE * ITER, bytes_cycle / 1000, bytes_cycle % 1000);
free(buf);
}
__NO_INLINE static void bench_memcpy(void)
{
uint8_t *buf = calloc(1, BUFSIZE);
uint count = arch_cycle_count();
for (uint i = 0; i < ITER; i++) {
memcpy(buf, buf + BUFSIZE / 2, BUFSIZE / 2);
}
count = arch_cycle_count() - count;
uint64_t bytes_cycle = (BUFSIZE / 2 * ITER * 1000ULL) / count;
printf("took %u cycles to memcpy a buffer of size %u %d times (%u source bytes), %llu.%03llu source bytes/cycle\n",
count, BUFSIZE / 2, ITER, BUFSIZE / 2 * ITER, bytes_cycle / 1000, bytes_cycle % 1000);
free(buf);
}
#if ARCH_ARM
__NO_INLINE static void arm_bench_cset_stm(void)
{
uint32_t *buf = malloc(BUFSIZE);
uint count = arch_cycle_count();
for (uint i = 0; i < ITER; i++) {
for (uint j = 0; j < BUFSIZE / sizeof(*buf) / 8; j++) {
__asm__ volatile(
"stm %0, {r0-r7};"
:: "r" (&buf[j*8])
);
}
}
count = arch_cycle_count() - count;
uint64_t bytes_cycle = (BUFSIZE * ITER * 1000ULL) / count;
printf("took %u cycles to manually clear a buffer of size %u %d times 8 words at a time using stm (%u bytes), %llu.%03llu bytes/cycle\n",
count, BUFSIZE, ITER, BUFSIZE * ITER, bytes_cycle / 1000, bytes_cycle % 1000);
free(buf);
}
#if (__CORTEX_M >= 0x03)
__NO_INLINE static void arm_bench_multi_issue(void)
{
uint32_t cycles;
uint32_t a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0;
#define ITER 1000000
uint count = ITER;
cycles = arch_cycle_count();
while (count--) {
__asm__ volatile ("");
__asm__ volatile ("add %0, %0, %0" : "=r" (a) : "r" (a));
__asm__ volatile ("add %0, %0, %0" : "=r" (b) : "r" (b));
__asm__ volatile ("and %0, %0, %0" : "=r" (c) : "r" (c));
__asm__ volatile ("mov %0, %0" : "=r" (d) : "r" (d));
__asm__ volatile ("orr %0, %0, %0" : "=r" (e) : "r" (e));
__asm__ volatile ("add %0, %0, %0" : "=r" (f) : "r" (f));
__asm__ volatile ("and %0, %0, %0" : "=r" (g) : "r" (g));
__asm__ volatile ("mov %0, %0" : "=r" (h) : "r" (h));
}
cycles = arch_cycle_count() - cycles;
uint64_t bytes_cycle = (cycles * 1000ULL) / ITER;
printf("took %u cycles to issue 8 integer ops (%llu.%03llu cycles/iteration)\n", cycles, bytes_cycle / 1000, bytes_cycle % 1000);
#undef ITER
}
#endif // __CORTEX_M
#endif // ARCH_ARM
#if WITH_LIB_LIBM && !WITH_NO_FP
#include <math.h>
__NO_INLINE static void bench_sincos(void)
{
printf("touching the floating point unit\n");
__UNUSED volatile double _hole = sin(0);
uint count = arch_cycle_count();
__UNUSED double a = sin(2.0);
count = arch_cycle_count() - count;
printf("took %u cycles for sin()\n", count);
count = arch_cycle_count();
a = cos(2.0);
count = arch_cycle_count() - count;
printf("took %u cycles for cos()\n", count);
count = arch_cycle_count();
a = sinf(2.0);
count = arch_cycle_count() - count;
printf("took %u cycles for sinf()\n", count);
count = arch_cycle_count();
a = cosf(2.0);
count = arch_cycle_count() - count;
printf("took %u cycles for cosf()\n", count);
count = arch_cycle_count();
a = sqrt(1234567.0);
count = arch_cycle_count() - count;
printf("took %u cycles for sqrt()\n", count);
count = arch_cycle_count();
a = sqrtf(1234567.0f);
count = arch_cycle_count() - count;
printf("took %u cycles for sqrtf()\n", count);
}
#endif // WITH_LIB_LIBM
void benchmarks(void)
{
bench_set_overhead();
bench_memset();
bench_memcpy();
bench_cset_uint8_t();
bench_cset_uint16_t();
bench_cset_uint32_t();
bench_cset_uint64_t();
bench_cset_wide();
#if ARCH_ARM
arm_bench_cset_stm();
#if (__CORTEX_M >= 0x03)
arm_bench_multi_issue();
#endif
#endif
#if WITH_LIB_LIBM && !WITH_NO_FP
bench_sincos();
#endif
}
+73
Ver Arquivo
@@ -0,0 +1,73 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#if ARM_WITH_CACHE
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arch.h>
#include <arch/ops.h>
#include <lib/console.h>
#include <platform.h>
static void bench_cache(size_t bufsize, uint8_t *buf)
{
lk_bigtime_t t;
bool do_free;
if (buf == 0) {
buf = memalign(PAGE_SIZE, bufsize);
do_free = true;
} else {
do_free = false;
}
printf("buf %p, size %zu\n", buf, bufsize);
if (!buf)
return;
t = current_time_hires();
arch_clean_cache_range((addr_t)buf, bufsize);
t = current_time_hires() - t;
printf("took %llu usecs to clean %d bytes (cold)\n", t, bufsize);
memset(buf, 0x99, bufsize);
t = current_time_hires();
arch_clean_cache_range((addr_t)buf, bufsize);
t = current_time_hires() - t;
if (do_free)
free(buf);
printf("took %llu usecs to clean %d bytes (hot)\n", t, bufsize);
}
static int cache_tests(int argc, const cmd_args *argv)
{
uint8_t *buf;
buf = (uint8_t *)((argc > 1) ? argv[1].u : 0UL);
printf("testing cache\n");
bench_cache(2*1024, buf);
bench_cache(64*1024, buf);
bench_cache(256*1024, buf);
bench_cache(1*1024*1024, buf);
bench_cache(8*1024*1024, buf);
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("cache_tests", "test/bench the cpu cache", &cache_tests)
STATIC_COMMAND_END(cache_tests);
#endif
+101
Ver Arquivo
@@ -0,0 +1,101 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdio.h>
#include <rand.h>
#include <err.h>
#include <app/tests.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include <kernel/semaphore.h>
#include <kernel/event.h>
#include <platform.h>
void clock_tests(void)
{
uint32_t c;
lk_time_t t;
lk_bigtime_t t2;
thread_sleep(100);
c = arch_cycle_count();
t = current_time();
c = arch_cycle_count() - c;
printf("%u cycles per current_time()\n", c);
thread_sleep(100);
c = arch_cycle_count();
t2 = current_time_hires();
c = arch_cycle_count() - c;
printf("%u cycles per current_time_hires()\n", c);
printf("making sure time never goes backwards\n");
{
printf("testing current_time()\n");
lk_time_t start = current_time();
lk_time_t last = start;
for (;;) {
t = current_time();
//printf("%lu %lu\n", last, t);
if (TIME_LT(t, last)) {
printf("WARNING: time ran backwards: %lu < %lu\n", t, last);
last = t;
continue;
}
last = t;
if (last - start > 5000)
break;
}
}
{
printf("testing current_time_hires()\n");
lk_bigtime_t start = current_time_hires();
lk_bigtime_t last = start;
for (;;) {
t2 = current_time_hires();
//printf("%llu %llu\n", last, t2);
if (t2 < last) {
printf("WARNING: time ran backwards: %llu < %llu\n", t2, last);
last = t2;
continue;
}
last = t2;
if (last - start > 5000000)
break;
}
}
printf("making sure current_time() and current_time_hires() are always the same base\n");
{
lk_time_t start = current_time();
for (;;) {
t = current_time();
t2 = current_time_hires();
if (t > ((t2 + 500) / 1000)) {
printf("WARNING: current_time() ahead of current_time_hires() %lu %llu\n", t, t2);
}
if (t - start > 5000)
break;
}
}
printf("counting to 5, in one second intervals\n");
for (int i = 0; i < 5; i++) {
thread_sleep(1000);
printf("%d\n", i + 1);
}
printf("measuring cpu clock against current_time_hires()\n");
for (int i = 0; i < 5; i++) {
uint cycles = arch_cycle_count();
lk_bigtime_t start = current_time_hires();
while ((current_time_hires() - start) < 1000000)
;
cycles = arch_cycle_count() - cycles;
printf("%u cycles per second\n", cycles);
}
}
+78
Ver Arquivo
@@ -0,0 +1,78 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdio.h>
#include <rand.h>
#include <err.h>
#include <app/tests.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include <kernel/semaphore.h>
#include <kernel/event.h>
#include <platform.h>
static int fibo_thread(void *argv)
{
long fibo = (intptr_t)argv;
thread_t *t[2];
if (fibo == 0)
return 0;
if (fibo == 1)
return 1;
char name[32];
snprintf(name, sizeof(name), "fibo %lu", fibo - 1);
t[0] = thread_create(name, &fibo_thread, (void *)(fibo - 1), DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
if (!t[0]) {
printf("error creating thread for fibo %d\n", fibo-1);
return 0;
}
snprintf(name, sizeof(name), "fibo %lu", fibo - 2);
t[1] = thread_create(name, &fibo_thread, (void *)(fibo - 2), DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
if (!t[1]) {
printf("error creating thread for fibo %d\n", fibo-2);
thread_resume(t[0]);
thread_join(t[0], NULL, INFINITE_TIME);
return 0;
}
thread_resume(t[0]);
thread_resume(t[1]);
int retcode0, retcode1;
thread_join(t[0], &retcode0, INFINITE_TIME);
thread_join(t[1], &retcode1, INFINITE_TIME);
return retcode0 + retcode1;
}
int fibo(int argc, const cmd_args *argv)
{
if (argc < 2) {
printf("not enough args\n");
return -1;
}
lk_time_t tim = current_time();
thread_t *t = thread_create("fibo", &fibo_thread, (void *)(uintptr_t)argv[1].u, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t);
int retcode;
thread_join(t, &retcode, INFINITE_TIME);
tim = current_time() - tim;
printf("fibo %d\n", retcode);
printf("took %u msecs to calculate\n", tim);
return NO_ERROR;
}
+101
Ver Arquivo
@@ -0,0 +1,101 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2013-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#if !WITH_NO_FP || ARM_WITH_VFP
#include <stdio.h>
#include <rand.h>
#include <err.h>
#include <lib/console.h>
#include <app/tests.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include <kernel/semaphore.h>
#include <kernel/event.h>
#include <platform.h>
extern void float_vfp_arm_instruction_test(void);
extern void float_vfp_thumb_instruction_test(void);
extern void float_neon_arm_instruction_test(void);
extern void float_neon_thumb_instruction_test(void);
/* optimize this function to cause it to try to use a lot of registers */
__OPTIMIZE("O3")
static int float_thread(void *arg)
{
double *val = arg;
uint i, j;
double a[16];
/* do a bunch of work with floating point to test context switching */
a[0] = *val;
for (i = 1; i < countof(a); i++) {
a[i] = a[i-1] * 1.01;
}
for (i = 0; i < 1000000; i++) {
a[0] += i;
for (j = 1; j < countof(a); j++) {
a[j] += a[j-1] * 0.00001;
}
}
*val = a[countof(a) - 1];
return 1;
}
#if ARCH_ARM
static void arm_float_instruction_trap_test(void)
{
printf("testing fpu trap\n");
#if !ARM_ONLY_THUMB
float_vfp_arm_instruction_test();
float_neon_arm_instruction_test();
#endif
float_vfp_thumb_instruction_test();
float_neon_thumb_instruction_test();
printf("if we got here, we probably decoded everything properly\n");
}
#endif
static void float_tests(void)
{
printf("floating point test:\n");
/* test lazy fpu load on separate thread */
thread_t *t[8];
double val[countof(t)];
printf("creating %u floating point threads\n", countof(t));
for (uint i = 0; i < countof(t); i++) {
val[i] = i;
t[i] = thread_create("float", &float_thread, &val[i], LOW_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t[i]);
}
int res;
for (uint i = 0; i < countof(t); i++) {
thread_join(t[i], &res, INFINITE_TIME);
printf("float thread %u returns %d, val %f\n", i, res, val[i]);
}
printf("the above values should be close\n");
#if ARCH_ARM
/* test all the instruction traps */
arm_float_instruction_trap_test();
#endif
}
STATIC_COMMAND_START
STATIC_COMMAND("float_tests", "floating point test", (console_cmd)&float_tests)
STATIC_COMMAND_END(float_tests);
#endif
+72
Ver Arquivo
@@ -0,0 +1,72 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <asm.h>
#if ARM_WITH_VFP
.fpu neon
.syntax unified
.macro disable, scratchreg
vmrs \scratchreg, fpexc
bic \scratchreg, #(1<<30)
vmsr fpexc, \scratchreg
.endm
.macro vfp_instructions
disable r12
vadd.f32 s0, s0, s0
disable r12
vadd.f64 d0, d0, d0
disable r12
ldr r0, =float_test_scratch
vldr s0, [r0]
.endm
.macro neon_instructions
disable r12
vadd.f32 q0, q0, q0
disable r12
ldr r0, =float_test_scratch
vld1.f32 { q0 }, [r0]
disable r12
vmov s0, r0
.endm
#if !ARM_ONLY_THUMB
.arm
FUNCTION(float_vfp_arm_instruction_test)
vfp_instructions
bx lr
FUNCTION(float_neon_arm_instruction_test)
neon_instructions
bx lr
#endif
.thumb
FUNCTION(float_vfp_thumb_instruction_test)
vfp_instructions
bx lr
FUNCTION(float_neon_thumb_instruction_test)
neon_instructions
bx lr
.data
LOCAL_DATA(float_test_scratch)
.word 0
.word 0
#endif // ARM_WITH_VFP
+23
Ver Arquivo
@@ -0,0 +1,23 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdio.h>
#define countof(a) (sizeof(a) / sizeof((a)[0]))
#include "float_test_vec.c"
int main(void)
{
printf("floating point printf tests\n");
for (size_t i = 0; i < float_test_vec_size; i++) {
PRINT_FLOAT;
}
return 0;
}
+66
Ver Arquivo
@@ -0,0 +1,66 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdint.h>
union double_int {
double d;
uint64_t i;
};
static const union double_int float_test_vec[] = {
{ .d = -2.0 },
{ .d = -1.0 },
{ .d = -0.5 },
{ .d = -0.0 },
{ .d = 0.0 },
{ .d = 0.01 },
{ .d = 0.1 },
{ .d = 0.2 },
{ .d = 0.25 },
{ .d = 0.5 },
{ .d = 0.75 },
{ .d = 1.0 },
{ .d = 2.0 },
{ .d = 3.0 },
{ .d = 10.0 },
{ .d = 100.0 },
{ .d = 123456.0 },
{ .d = -123456.0 },
{ .d = 546.5645644531f },
{ .d = -546.5645644531f },
{ .d = 0.12345 },
{ .d = 0.0000012345 },
{ .d = 0.0000019999 },
{ .d = 0.0000015 },
{ .i = 0x4005bf0a8b145649ULL }, // e
{ .i = 0x400921fb54442d18ULL }, // pi
{ .i = 0x43f0000000000000ULL }, // 2^64
{ .i = 0x7fefffffffffffffULL }, // largest normalized
{ .i = 0x0010000000000000ULL }, // least positive normalized
{ .i = 0x0000000000000001ULL }, // smallest possible denorm
{ .i = 0x000fffffffffffffULL }, // largest possible denorm
{ .i = 0x7ff0000000000001ULL }, // smallest SNAn
{ .i = 0x7ff7ffffffffffffULL }, // largest SNAn
{ .i = 0x7ff8000000000000ULL }, // smallest QNAn
{ .i = 0x7fffffffffffffffULL }, // largest QNAn
{ .i = 0xfff0000000000000ULL }, // -infinity
{ .i = 0x7ff0000000000000ULL }, // +infinity
};
#define countof(a) (sizeof(a) / sizeof((a)[0]))
static const unsigned int float_test_vec_size = countof(float_test_vec);
#define PRINT_FLOAT \
printf("0x%016llx %f %F %a %A\n", \
float_test_vec[i], \
*(const double *)&float_test_vec[i], \
*(const double *)&float_test_vec[i], \
*(const double *)&float_test_vec[i], \
*(const double *)&float_test_vec[i])
+94
Ver Arquivo
@@ -0,0 +1,94 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <stdio.h>
#include <unittest.h>
#include <utils/type_support.h>
enum Category { CAT_LVALUE, CAT_RVALUE };
int category(const int& arg) { return CAT_LVALUE; }
int category(int&& arg) { return CAT_RVALUE; }
template <typename T>
int passing(T&& t)
{
return category(t);
}
template <typename T>
int moving(T&& t)
{
return category(utils::move(t));
}
template <typename T>
int forwarding(T&& t)
{
return category(utils::forward<T>(t));
}
template <typename T>
int forward_copy(T&& t)
{
return category(utils::forward<T&>(t));
}
struct A {
A(int&& n) : category(CAT_RVALUE) {}
A(int& n) : category(CAT_LVALUE) {}
int category;
};
template <typename T, typename U>
T make_object(U&& u)
{
return T(utils::forward<U>(u));
}
extern "C" int forward_tests(int argc, const cmd_args* argv)
{
bool all_ok = true;
int val = 42;
int& ref = val;
const int& cref = val;
EXPECT_EQ(CAT_LVALUE, passing(42), "");
EXPECT_EQ(CAT_LVALUE, passing(val), "");
EXPECT_EQ(CAT_LVALUE, passing(ref), "");
EXPECT_EQ(CAT_LVALUE, passing(cref), "");
EXPECT_EQ(CAT_LVALUE, passing(val + 1), "");
EXPECT_EQ(CAT_RVALUE, moving(42), "");
EXPECT_EQ(CAT_RVALUE, moving(val), "");
EXPECT_EQ(CAT_RVALUE, moving(ref), "");
EXPECT_EQ(CAT_LVALUE, moving(cref), "");
EXPECT_EQ(CAT_RVALUE, moving(val + 1), "");
EXPECT_EQ(CAT_RVALUE, forwarding(42), "");
EXPECT_EQ(CAT_LVALUE, forwarding(val), "");
EXPECT_EQ(CAT_LVALUE, forwarding(ref), "");
EXPECT_EQ(CAT_LVALUE, forward_copy(cref), "");
EXPECT_EQ(CAT_RVALUE, forwarding(val + 1), "");
EXPECT_EQ(CAT_LVALUE, forward_copy(42), "");
EXPECT_EQ(CAT_LVALUE, forward_copy(val), "");
EXPECT_EQ(CAT_LVALUE, forward_copy(ref), "");
EXPECT_EQ(CAT_LVALUE, forward_copy(cref), "");
EXPECT_EQ(CAT_LVALUE, forward_copy(val + 1), "");
auto a1 = make_object<A>(42);
auto a2 = make_object<A>(val);
EXPECT_EQ(CAT_RVALUE, a1.category, "");
EXPECT_EQ(CAT_LVALUE, a2.category, "");
if (all_ok) printf("all tests passed\n");
return 0;
}
+80
Ver Arquivo
@@ -0,0 +1,80 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <unittest.h>
#include <utils/hash_table.h>
class FooHash {
public:
FooHash(int value) : key_(-1), value_(value), next_(nullptr) {}
int get_value() const { return value_; }
int get_key() const { return key_; }
void set_key(int key) { key_ = key; }
void list_set_next(FooHash* foo) { next_ = foo; }
const FooHash* list_next() const { return next_; }
FooHash* list_next() { return next_; }
private:
int key_;
int value_;
FooHash* next_;
};
struct FooHashFn {
size_t operator()(int key) const
{
auto s = reinterpret_cast<char*>(&key);
auto p = s;
size_t h = 1;
for (int ix = 0; ix != sizeof(int); ++p, ++ix) {
h = ((h << 4) * 17) + *p;
}
return h;
}
};
int GetHashTableKey(FooHash* fh) { return fh->get_key(); }
void SetHashTableKey(FooHash* fh, int key) { fh->set_key(key); }
extern "C" int hash_tests(int argc, const cmd_args* argv)
{
bool all_ok = true;
utils::HashTable<int, FooHash, FooHashFn> foo_table;
const size_t count = 5;
int keys[count] = {50, 500, 5000, 50000, 500000};
FooHash foo[count] = {1, 2, 3, 4, 5};
for (size_t ix = 0; ix != count; ++ix) {
foo_table.add(keys[ix], &foo[ix]);
}
EXPECT_EQ(count, foo_table.size(), "");
int sum = 0;
foo_table.for_each([&sum](FooHash* f) {
sum += f->get_value();
});
EXPECT_EQ(15, sum, "");
auto item = foo_table.remove(5000);
EXPECT_EQ(3, item->get_value(), "");
EXPECT_EQ(count - 1, foo_table.size(), "");
EXPECT_TRUE(nullptr == foo_table.remove(5000), "");
foo_table.clear();
EXPECT_EQ(0U, foo_table.size(), "");
printf("hash tests : %s\n", all_ok ? "ok" : "failed");
return 0;
}
+38
Ver Arquivo
@@ -0,0 +1,38 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef __APP_TESTS_H
#define __APP_TESTS_H
#include <compiler.h>
#include <lib/console.h>
__BEGIN_CDECLS
int thread_tests(void);
int port_tests(void);
void printf_tests(void);
void clock_tests(void);
void benchmarks(void);
int fibo(int argc, const cmd_args *argv);
int spinner(int argc, const cmd_args *argv);
int ref_counted_tests(int argc, const cmd_args *argv);
int ref_ptr_tests(int argc, const cmd_args *argv);
int unique_ptr_tests(int argc, const cmd_args *argv);
int forward_tests(int argc, const cmd_args *argv);
int list_tests(int argc, const cmd_args *argv);
int hash_tests(int argc, const cmd_args *argv);
int vm_tests(int argc, const cmd_args *argv);
int auto_call_tests(int argc, const cmd_args *argv);
int sync_ipi_tests(int argc, const cmd_args *argv);
int arena_tests(int argc, const cmd_args *argv);
void unittests(void);
__END_CDECLS
#endif
+279
Ver Arquivo
@@ -0,0 +1,279 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <unittest.h>
#include <utils/intrusive_double_list.h>
#include <utils/intrusive_single_list.h>
#include <utils/list_utils.h>
class TestValue {
public:
TestValue(int val) : val_(val) {}
int value() const { return val_; }
private:
int val_;
};
class Foo : public TestValue {
public:
Foo(int val) : TestValue(val), next_(nullptr) {}
void list_set_next(Foo* foo) { next_ = foo; }
const Foo* list_next() const { return next_; }
Foo* list_next() { return next_; }
private:
Foo* next_;
};
class Bar : public TestValue {
public:
Bar(int val) : TestValue(val), next_l1_(nullptr), next_l2_(nullptr) {}
struct List1Traits {
static Bar* next(Bar* bar) { return bar->next_l1_; }
static const Bar* next(const Bar* bar) { return bar->next_l1_; }
static void set_next(Bar* bar, Bar* next) { bar->next_l1_ = next; }
};
struct List2Traits {
static Bar* next(Bar* bar) { return bar->next_l2_; }
static const Bar* next(const Bar* bar) { return bar->next_l2_; }
static void set_next(Bar* bar, Bar* next) { bar->next_l2_ = next; }
};
private:
Bar* next_l1_;
Bar* next_l2_;
};
class Baz : public TestValue {
public:
Baz(int val) : TestValue(val), next_(nullptr), prev_(nullptr) {}
Baz* list_prev() { return prev_; }
const Baz* list_prev() const { return prev_; }
Baz* list_next() { return next_; }
const Baz* list_next() const { return next_; }
void list_set_prev(Baz* prev) { prev_ = prev; }
void list_set_next(Baz* next) { next_ = next; }
private:
Baz* next_;
Baz* prev_;
};
static bool all_ok = true;
static void singly_linked_one_list_test() {
const size_t count = 7;
int prev = 0;
Foo foo[count] = {1, 2, 3, 4, 5, 6, 7};
utils::SinglyLinkedList<Foo> slist;
EXPECT_EQ(0U, slist.size_slow(), "");
for (int ix = 0; ix != count; ++ix) {
slist.push_front(&foo[ix]);
}
EXPECT_EQ(count, slist.size_slow(), "");
Foo* found = utils::find_if(&slist, [](Foo* f) {
return f->value() == 4;
});
EXPECT_TRUE(found == &foo[3], "");
int sum = 0;
utils::for_each(&slist, [&sum](Foo* f) { sum += f->value(); });
EXPECT_EQ(28, sum, "");
prev = 8;
for (int ix = 0; ix != count; ++ix) {
Foo* foo = slist.pop_front();
EXPECT_TRUE(prev > foo->value(), "");
prev = foo->value();
}
EXPECT_TRUE(slist.is_empty(), "");
slist.push_front(&foo[2]);
slist.push_front(&foo[3]);
slist.push_front(&foo[4]);
found = utils::pop_if(&slist, [](Foo* f) {
return f->value() == 4;
});
EXPECT_EQ(2U, slist.size_slow(), "");
found = utils::pop_if(&slist, [](Foo* f) {
return f->value() == 3;
});
EXPECT_EQ(1U, slist.size_slow(), "");
slist.clear();
}
void singly_linked_two_lists_test() {
const size_t count = 7;
int prev = 0;
Bar bar[count] = {7, 6, 5, 4, 3, 2, 1};
utils::SinglyLinkedList<Bar, Bar::List1Traits> slist1;
utils::SinglyLinkedList<Bar, Bar::List2Traits> slist2;
for (int ix = 0; ix != count; ++ix) {
slist1.push_front(&bar[ix]);
slist2.push_front(&bar[ix]);
}
EXPECT_EQ(count, slist1.size_slow(), "");
EXPECT_EQ(count, slist2.size_slow(), "");
prev = 0;
for (int ix = 0; ix != count; ++ix) {
Bar* bar = slist1.pop_front();
EXPECT_TRUE(prev < bar->value(), "");
prev = bar->value();
}
prev = 0;
for (int ix = 0; ix != count; ++ix) {
Bar* bar = slist2.pop_front();
EXPECT_TRUE(prev < bar->value(), "");
prev = bar->value();
}
EXPECT_TRUE(slist1.is_empty(), "");
EXPECT_TRUE(slist2.is_empty(), "");
}
void doubly_linked_one_list_test() {
const size_t count = 5;
Baz baz[count] = {1, 2, 3, 4, 5};
utils::DoublyLinkedList<Baz> dlist;
EXPECT_EQ(0U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.pop_front() == nullptr, "");
EXPECT_TRUE(dlist.pop_back() == nullptr, "");
dlist.push_front(&baz[0]);
EXPECT_EQ(1U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.pop_back() == &baz[0], "");
EXPECT_EQ(0U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.is_empty(), "");
dlist.push_back(&baz[1]);
EXPECT_EQ(1U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.pop_front() == &baz[1], "");
EXPECT_EQ(0U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.is_empty(), "");
dlist.push_back(&baz[0]);
dlist.push_back(&baz[1]);
EXPECT_TRUE(dlist.pop_front() == &baz[0], "");
EXPECT_TRUE(dlist.pop_front() == &baz[1], "");
EXPECT_EQ(0U, dlist.size_slow(), "");
for (int ix = 0; ix != count; ++ix) {
dlist.push_front(&baz[ix]);
}
EXPECT_EQ(5U, dlist.size_slow(), "");
int sum = 0;
for_each(&dlist, [&sum](Baz* b) { sum += b->value(); });
EXPECT_EQ(15, sum, "");
int prev = 6;
for (int ix = 0; ix != count; ++ix) {
auto baz = dlist.pop_front();
EXPECT_TRUE(prev > baz->value(), "");
prev = baz->value();
}
EXPECT_EQ(0U, dlist.size_slow(), "");
for (int ix = 0; ix != count; ++ix) {
dlist.push_back(&baz[ix]);
}
EXPECT_EQ(5U, dlist.size_slow(), "");
prev = 6;
for (int ix = 0; ix != count; ++ix) {
auto baz = dlist.pop_back();
EXPECT_TRUE(prev > baz->value(), "");
prev = baz->value();
}
EXPECT_EQ(0U, dlist.size_slow(), "");
dlist.push_back(&baz[2]);
dlist.push_back(&baz[3]);
dlist.remove(dlist.first());
EXPECT_EQ(1U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.first() == &baz[3], "");
dlist.remove(dlist.first());
EXPECT_EQ(0U, dlist.size_slow(), "");
EXPECT_TRUE(dlist.first() == nullptr, "");
dlist.clear();
}
void list_move_test()
{
const size_t count = 7;
Foo foo[count] = {1, 2, 3, 4, 5, 6, 7};
utils::SinglyLinkedList<Foo> slist_src;
for (int ix = 0; ix != count; ++ix) {
slist_src.push_front(&foo[ix]);
}
utils::SinglyLinkedList<Foo> slist_dst;
utils::move_if(&slist_src, &slist_dst, [](Foo* foo) {
return ((foo->value() % 2) == 0);
});
EXPECT_EQ(3U, slist_dst.size_slow(), "");
EXPECT_EQ(4U, slist_src.size_slow(), "");
utils::move_if(&slist_dst, &slist_src, [](Foo* foo) {
return ((foo->value() % 2) == 0);
});
EXPECT_EQ(0U, slist_dst.size_slow(), "");
EXPECT_EQ(7U, slist_src.size_slow(), "");
slist_src.clear();
}
extern "C" int list_tests(int argc, const cmd_args* argv)
{
singly_linked_one_list_test();
singly_linked_two_lists_test();
doubly_linked_one_list_test();
list_move_test();
printf("list tests : %s\n", all_ok ? "ok" : "failed");
return 0;
}
+227
Ver Arquivo
@@ -0,0 +1,227 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <arch.h>
#include <arch/ops.h>
#include <lib/console.h>
#include <platform.h>
#include <debug.h>
#if WITH_KERNEL_VM
#include <kernel/vm.h>
#endif
static void mem_test_fail(void *ptr, uint32_t should, uint32_t is)
{
printf("ERROR at %p: should be 0x%x, is 0x%x\n", ptr, should, is);
ptr = (void *)ROUNDDOWN((uintptr_t)ptr, 64);
hexdump(ptr, 128);
}
static status_t do_pattern_test(void *ptr, size_t len, uint32_t pat)
{
volatile uint32_t *vbuf32 = ptr;
size_t i;
printf("\tpattern 0x%08x\n", pat);
for (i = 0; i < len / 4; i++) {
vbuf32[i] = pat;
}
for (i = 0; i < len / 4; i++) {
if (vbuf32[i] != pat) {
mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
return ERR_GENERIC;
}
}
return NO_ERROR;
}
static status_t do_moving_inversion_test(void *ptr, size_t len, uint32_t pat)
{
volatile uint32_t *vbuf32 = ptr;
size_t i;
printf("\tpattern 0x%08x\n", pat);
/* fill memory */
for (i = 0; i < len / 4; i++) {
vbuf32[i] = pat;
}
/* from the bottom, walk through each cell, inverting the value */
//printf("\t\tbottom up invert\n");
for (i = 0; i < len / 4; i++) {
if (vbuf32[i] != pat) {
mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
return ERR_GENERIC;
}
vbuf32[i] = ~pat;
}
/* repeat, walking from top down */
//printf("\t\ttop down invert\n");
for (i = len / 4; i > 0; i--) {
if (vbuf32[i-1] != ~pat) {
mem_test_fail((void *)&vbuf32[i-1], ~pat, vbuf32[i-1]);
return ERR_GENERIC;
}
vbuf32[i-1] = pat;
}
/* verify that we have the original pattern */
//printf("\t\tfinal test\n");
for (i = 0; i < len / 4; i++) {
if (vbuf32[i] != pat) {
mem_test_fail((void *)&vbuf32[i], pat, vbuf32[i]);
return ERR_GENERIC;
}
}
return NO_ERROR;
}
static void do_mem_tests(void *ptr, size_t len)
{
size_t i;
/* test 1: simple write address to memory, read back */
printf("test 1: simple address write, read back\n");
volatile uint32_t *vbuf32 = ptr;
for (i = 0; i < len / 4; i++) {
vbuf32[i] = i;
}
for (i = 0; i < len / 4; i++) {
if (vbuf32[i] != i) {
mem_test_fail((void *)&vbuf32[i], i, vbuf32[i]);
goto out;
}
}
/* test 2: write various patterns, read back */
printf("test 2: write patterns, read back\n");
static const uint32_t pat[] = {
0x0, 0xffffffff,
0xaaaaaaaa, 0x55555555,
};
for (size_t p = 0; p < countof(pat); p++) {
if (do_pattern_test(ptr, len, pat[p]) < 0)
goto out;
}
// shift bits through 32bit word
for (uint32_t p = 1; p != 0; p <<= 1) {
if (do_pattern_test(ptr, len, p) < 0)
goto out;
}
// shift bits through 16bit word, invert top of 32bit
for (uint16_t p = 1; p != 0; p <<= 1) {
if (do_pattern_test(ptr, len, ((~p) << 16) | p) < 0)
goto out;
}
/* test 3: moving inversion, patterns */
printf("test 3: moving inversions with patterns\n");
for (size_t p = 0; p < countof(pat); p++) {
if (do_moving_inversion_test(ptr, len, pat[p]) < 0)
goto out;
}
// shift bits through 32bit word
for (uint32_t p = 1; p != 0; p <<= 1) {
if (do_moving_inversion_test(ptr, len, p) < 0)
goto out;
}
// shift bits through 16bit word, invert top of 32bit
for (uint16_t p = 1; p != 0; p <<= 1) {
if (do_moving_inversion_test(ptr, len, ((~p) << 16) | p) < 0)
goto out;
}
out:
printf("done with tests\n");
}
static int mem_test(int argc, const cmd_args *argv)
{
if (argc < 2) {
printf("not enough arguments\n");
usage:
printf("usage: %s <length>\n", argv[0].str);
printf("usage: %s <base> <length>\n", argv[0].str);
return -1;
}
if (argc == 2) {
void *ptr;
size_t len = argv[1].u;
#if WITH_KERNEL_VM
/* rounding up len to the next page */
len = PAGE_ALIGN(len);
if (len == 0) {
printf("invalid length\n");
return -1;
}
/* allocate a region to test in */
status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "memtest", len, &ptr, 0, 0, ARCH_MMU_FLAG_UNCACHED);
if (err < 0) {
printf("error %d allocating test region\n", err);
return -1;
}
paddr_t pa;
pa = vaddr_to_paddr(ptr);
printf("physical address 0x%lx\n", pa);
#else
/* allocate from the heap */
ptr = malloc(len);
if (!ptr ) {
printf("error allocating test area from heap\n");
return -1;
}
#endif
printf("got buffer at %p of length 0x%lx\n", ptr, len);
/* run the tests */
do_mem_tests(ptr, len);
#if WITH_KERNEL_VM
// XXX free memory region here
printf("NOTE: leaked memory\n");
#else
free(ptr);
#endif
} else if (argc == 3) {
void *ptr = argv[1].p;
size_t len = argv[2].u;
/* run the tests */
do_mem_tests(ptr, len);
} else {
goto usage;
}
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("mem_test", "test memory", &mem_test)
STATIC_COMMAND_END(mem_tests);
+836
Ver Arquivo
@@ -0,0 +1,836 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2015 Carlos Pizano-Uribe cpu@chromium.org
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <err.h>
#include <rand.h>
#include <string.h>
#include <trace.h>
#include <kernel/event.h>
#include <kernel/port.h>
#include <kernel/thread.h>
#include <platform.h>
#define LOCAL_TRACE 0
void *context1 = (void *) 0x53;
static void dump_port_result(const port_result_t *result)
{
const port_packet_t *p = &result->packet;
LTRACEF("[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
p->value[0], p->value[1], p->value[2], p->value[3],
p->value[4], p->value[5], p->value[6], p->value[7]);
}
static int single_thread_basic(void)
{
port_t w_port;
status_t st = port_create("sh_prt1", PORT_MODE_UNICAST, &w_port);
if (st < 0) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
port_t r_port;
st = port_open("sh_prt0", context1, &r_port);
if (st != ERR_NOT_FOUND) {
printf("expected not to find port, status = %d\n", st);
return __LINE__;
}
st = port_open("sh_prt1", context1, &r_port);
if (st < 0) {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
port_packet_t packet[3] = {
{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}},
{{{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11}}},
{{{0x33, 0x66, 0x99, 0xcc, 0x33, 0x66, 0x99, 0xcc}}},
};
st = port_write(w_port, &packet[0], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
port_result_t res = {0};
st = port_peek(r_port, &res);
if (st < 0) {
printf("could not peek port, status = %d\n", st);
return __LINE__;
}
if (res.ctx != context1) {
printf("bad context! = %p\n", res.ctx);
return __LINE__;
}
res.ctx = NULL;
st = port_read(r_port, 0, &res);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
if (res.ctx != context1) {
printf("bad context! = %p\n", res.ctx);
return __LINE__;
}
st = port_peek(r_port, &res);
if (st != ERR_NOT_READY) {
printf("expected 'not ready', status = %d\n", st);
return __LINE__;
}
st = port_read(r_port, 0, &res);
if (st != ERR_TIMED_OUT) {
printf("expected timeout, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet[1], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet[0], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet[2], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
int expected_count = 3;
while (true) {
st = port_read(r_port, 0, &res);
if (st < 0)
break;
dump_port_result(&res);
--expected_count;
}
if (expected_count != 0) {
printf("invalid read count = %d\n", expected_count);
return __LINE__;
}
// port should be empty. should be able to write 8 packets.
expected_count = 8;
while (true) {
st = port_write(w_port, &packet[1], 1);
if (st < 0)
break;
--expected_count;
st = port_write(w_port, &packet[2], 1);
if (st < 0)
break;
--expected_count;
}
if (expected_count != 0) {
printf("invalid write count = %d\n", expected_count);
return __LINE__;
}
// LK 'classic' ports return ERR_PARTIAL_WRITE.
if (st != ERR_CHANNEL_CLOSED) {
printf("expected buffer error, status =%d\n", st);
return __LINE__;
}
// read 3 packets.
for (int ix = 0; ix != 3; ++ix) {
st = port_read(r_port, 0, &res);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
}
// there are 5 packets, now we add another 3.
st = port_write(w_port, packet, 3);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
expected_count = 8;
while (true) {
st = port_read(r_port, 0, &res);
if (st < 0)
break;
dump_port_result(&res);
--expected_count;
}
if (expected_count != 0) {
printf("invalid read count = %d\n", expected_count);
return __LINE__;
}
// attempt to use the wrong port.
st = port_write(r_port, &packet[1], 1);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_read(w_port, 0, &res);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_close(r_port);
if (st < 0) {
printf("could not close read port, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st < 0) {
printf("could not close write port, status = %d\n", st);
return __LINE__;
}
st = port_close(r_port);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_destroy(w_port);
if (st < 0) {
printf("could not destroy port, status = %d\n", st);
return __LINE__;
}
printf("single_thread_basic : ok\n");
return 0;
}
static int single_thread_wait(void)
{
port_t w_port;
status_t st = port_create("sh_prt3", PORT_MODE_UNICAST, &w_port);
if (st < 0) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
port_t r_port;
st = port_open("sh_prt3", context1, &r_port);
if (st < 0) {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
port_packet_t packet = {{{0}}};
st = port_wait(r_port, 0);
if (st != ERR_TIMED_OUT) {
printf("expected timeout, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet, 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
st = port_wait(r_port, 100);
if (st < 0) {
printf("unexpected end of wait, status = %d\n", st);
return __LINE__;
}
port_result_t res = {0};
st = port_read(r_port, 0, &res);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
st = port_wait(r_port, 10);
if (st != ERR_TIMED_OUT) {
printf("expected timeout, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st < 0)
return __LINE__;
st = port_destroy(w_port);
if (st < 0)
return __LINE__;
st = port_wait(r_port, 0);
if (st != ERR_CHANNEL_CLOSED) {
printf("expected close, status = %d\n", st);
return __LINE__;
}
st = port_close(r_port);
if (st < 0)
return __LINE__;
printf("single_thread_wait : ok\n");
return 0;
}
static int ping_pong_thread(void *arg)
{
port_t r_port;
status_t st = port_open("ping_port", NULL, &r_port);
if (st < 0) {
printf("thread: could not open port, status = %d\n", st);
return __LINE__;
}
bool should_dispose_pong_port = true;
port_t w_port;
st = port_create("pong_port", PORT_MODE_UNICAST, &w_port);
if (st == ERR_ALREADY_EXISTS) {
// won the race to create the port.
should_dispose_pong_port = false;
} else if (st < 0) {
printf("thread: could not open port, status = %d\n", st);
return __LINE__;
}
port_result_t pr;
// the loop is read-mutate-write until the write port
// is closed by the master thread.
while (true) {
st = port_read(r_port, INFINITE_TIME, &pr);
if (st == ERR_CHANNEL_CLOSED) {
break;
} else if (st < 0) {
printf("thread: could not read port, status = %d\n", st);
return __LINE__;
}
pr.packet.value[0]++;
pr.packet.value[5]--;
st = port_write(w_port, &pr.packet, 1);
if (st < 0) {
printf("thread: could not write port, status = %d\n", st);
return __LINE__;
}
}
port_close(r_port);
if (should_dispose_pong_port) {
port_close(w_port);
port_destroy(w_port);
}
return 0;
bail:
return __LINE__;
}
int two_threads_basic(void)
{
port_t w_port;
status_t st = port_create("ping_port", PORT_MODE_BROADCAST, &w_port);
if (st < 0) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
thread_t *t1 = thread_create(
"worker1", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_t *t2 = thread_create(
"worker2", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t1);
thread_resume(t2);
// wait for the pong port to be created, the two threads race to do it.
port_t r_port;
while (true) {
status_t st = port_open("pong_port", NULL, &r_port);
if (st == NO_ERROR) {
break;
} else if (st == ERR_NOT_FOUND) {
thread_sleep(100);
} else {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
}
// We have two threads listening to the ping port. Which both reply
// on the pong port, so we get two packets in per packet out.
const int passes = 256;
printf("two_threads_basic test, %d passes\n", passes);
port_packet_t packet_out = {{{0xaf, 0x77, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}}};
port_result_t pr;
for (int ix = 0; ix != passes; ++ix) {
const size_t count = 1 + ((unsigned int)rand() % 3);
for (size_t jx = 0; jx != count; ++jx) {
st = port_write(w_port, &packet_out, 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
}
packet_out.value[0]++;
packet_out.value[5]--;
for (size_t jx = 0; jx != count * 2; ++jx) {
st = port_read(r_port, INFINITE_TIME, &pr);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
if ((pr.packet.value[0] != packet_out.value[0]) ||
(pr.packet.value[5] != packet_out.value[5])) {
printf("unexpected data in packet, loop %d", ix);
return __LINE__;
}
}
}
thread_sleep(100);
// there should be no more packets to read.
st = port_read(r_port, 0, &pr);
if (st != ERR_TIMED_OUT) {
printf("unexpected packet, status = %d\n", st);
return __LINE__;
}
printf("two_threads_basic master shutdown\n");
st = port_close(r_port);
if (st < 0) {
printf("could not close port, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st < 0) {
printf("could not close port, status = %d\n", st);
return __LINE__;
}
st = port_destroy(w_port);
if (st < 0) {
printf("could not destroy port, status = %d\n", st);
return __LINE__;
}
int retcode = -1;
thread_join(t1, &retcode, INFINITE_TIME);
if (retcode)
goto fail;
thread_join(t2, &retcode, INFINITE_TIME);
if (retcode)
goto fail;
printf("two_threads_basic : ok\n");
return 0;
fail:
printf("child thread exited with %d\n", retcode);
return __LINE__;
}
#define CMD_PORT_CTX ((void*) 0x77)
#define TS1_PORT_CTX ((void*) 0x11)
#define TS2_PORT_CTX ((void*) 0x12)
typedef enum {
NONE,
ADD_PORT,
QUIT
} action_t;
status_t send_watcher_cmd(port_t cmd_port, action_t action, port_t port)
{
size_t count = 1;
port_packet_t pp[2];
pp[0].value[0] = (char) action;
if (port) {
count = 2;
pp[1].pvalue = port;
}
return port_write(cmd_port, pp, count);
}
static int group_watcher_thread(void *arg)
{
port_t watched[8] = {0};
status_t st = port_open("grp_ctrl", CMD_PORT_CTX, &watched[0]);
if (st < 0) {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
size_t count = 1;
port_t group;
int ctx_count = -1;
while (true) {
st = port_group(watched, count, &group);
if (st < 0) {
printf("could not make group, status = %d\n", st);
return __LINE__;
}
port_result_t pr;
while (true) {
st = port_read(group, INFINITE_TIME, &pr);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
if (pr.ctx == CMD_PORT_CTX) {
break;
} else if (pr.ctx == TS1_PORT_CTX) {
ctx_count += 1;
} else if (pr.ctx == TS2_PORT_CTX) {
ctx_count += 2;
} else {
printf("unknown context %p\n", pr.ctx);
return __LINE__;
}
}
// Either adding a port or exiting; either way close the
// existing group port and create a new one if needed
// at the top of the loop.
st = port_close(group);
if (st <0) {
printf("could not close port, status = %d\n", st);
return __LINE__;
}
action_t action = pr.packet.value[0];
if (action == ADD_PORT) {
// There is a second packet for the "add port" action.
st = port_read(watched[0], 0, &pr);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
watched[count++] = pr.packet.pvalue;
} else if (action == QUIT) {
break;
} else {
printf("unknown action %d\n", action);
return __LINE__;
}
}
if (ctx_count != 2) {
printf("unexpected context count %d", ctx_count);
return __LINE__;
}
for (size_t ix = 0; ix != count; ++ix) {
st = port_close(watched[ix]);
if (st < 0) {
printf("failed to close read port, status = %d\n", st);
return __LINE__;
}
}
return 0;
}
static status_t make_port_pair(const char *name, void *ctx, port_t *write, port_t *read)
{
status_t st = port_create(name, PORT_MODE_UNICAST, write);
if (st < 0)
return st;
return port_open(name, ctx, read);
}
int group_basic(void)
{
// we spin a thread that connects to a well known port, then we
// send two ports that it will add to a group port.
port_t cmd_port;
status_t st = port_create("grp_ctrl", PORT_MODE_UNICAST, &cmd_port);
if (st < 0 ) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
thread_t *wt = thread_create(
"g_watcher", &group_watcher_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(wt);
port_t w_test_port1, r_test_port1;
st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
if (st < 0)
return __LINE__;
port_t w_test_port2, r_test_port2;
st = make_port_pair("tst_port2", TS2_PORT_CTX, &w_test_port2, &r_test_port2);
if (st < 0)
return __LINE__;
st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port1);
if (st < 0)
return __LINE__;
st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port2);
if (st < 0)
return __LINE__;
thread_sleep(50);
port_packet_t pp = {{{0}}};
st = port_write(w_test_port1, &pp, 1);
if (st < 0)
return __LINE__;
st = port_write(w_test_port2, &pp, 1);
if (st < 0)
return __LINE__;
st = send_watcher_cmd(cmd_port, QUIT, 0);
if (st < 0)
return __LINE__;
int retcode = -1;
thread_join(wt, &retcode, INFINITE_TIME);
if (retcode) {
printf("child thread exited with %d\n", retcode);
return __LINE__;
}
st = port_close(w_test_port1);
if (st < 0)
return __LINE__;
st = port_close(w_test_port2);
if (st < 0)
return __LINE__;
st = port_close(cmd_port);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port1);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port2);
if (st < 0)
return __LINE__;
st = port_destroy(cmd_port);
if (st < 0)
return __LINE__;
printf("group_basic : ok\n");
return 0;
}
int group_dynamic(void)
{
status_t st;
port_t w_test_port1, r_test_port1;
st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
if (st < 0)
return __LINE__;
port_t w_test_port2, r_test_port2;
st = make_port_pair("tst_port2", TS2_PORT_CTX, &w_test_port2, &r_test_port2);
if (st < 0)
return __LINE__;
port_t pg;
st = port_group(&r_test_port1, 1, &pg);
if (st < 0)
return __LINE__;
port_packet_t pkt = {{{ 0 }}};
st = port_write(w_test_port2, &pkt, 1);
if (st < 0)
return __LINE__;
port_result_t rslt;
st = port_read(pg, 0, &rslt);
if (st != ERR_TIMED_OUT)
return __LINE__;
// Attach the port that has been written to to the port group and ensure
// that we can read from it.
st = port_group_add(pg, r_test_port2);
if (st < 0)
return __LINE__;
st = port_read(pg, 0, &rslt);
if (st < 0)
return __LINE__;
// Write some data to a port then remove it from the port group and ensure
// that we can't read from it.
st = port_write(w_test_port1, &pkt, 1);
if (st < 0)
return __LINE__;
st = port_group_remove(pg, r_test_port1);
if (st < 0)
return __LINE__;
st = port_read(pg, 0, &rslt);
if (st != ERR_TIMED_OUT)
return __LINE__;
st = port_close(w_test_port1);
if (st < 0)
return __LINE__;
st = port_close(w_test_port2);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port1);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port2);
if (st < 0)
return __LINE__;
printf("group_dynamic : ok\n");
return 0;
}
event_t group_waiting_sync_evt;
static int receive_thread(void *arg)
{
port_t pg = (port_t)arg;
// Try to read from an empty port group. When the other thread adds a port
// to this port group, we should wake up and
port_result_t rslt;
status_t st = port_read(pg, 500, &rslt);
if (st == ERR_TIMED_OUT)
return __LINE__;
event_signal(&group_waiting_sync_evt, true);
return 0;
}
/* Test the edge case where a read port with data available is added to a port
* group that has a read-blocked receiver.
*/
int group_waiting(void)
{
status_t st;
event_init(&group_waiting_sync_evt, false, EVENT_FLAG_AUTOUNSIGNAL);
port_t w_test_port1, r_test_port1;
st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
if (st < 0)
return __LINE__;
// Write something to this port group that currently has no receivers.
port_packet_t pkt = {{{ 0 }}};
st = port_write(w_test_port1, &pkt, 1);
if (st < 0)
return __LINE__;
// Create an empty port group.
port_t pg;
st = port_group(NULL, 0, &pg);
if (st < 0)
return __LINE__;
thread_t *t1 = thread_create(
"receiver",
&receive_thread,
(void *)pg,
DEFAULT_PRIORITY,
DEFAULT_STACK_SIZE
);
thread_resume(t1);
// Wait for the other thread to block on the read.
thread_sleep(20);
// Adding a port that has data available to the port group should wake any
// threads waiting on that port group.
port_group_add(pg, r_test_port1);
if (event_wait_timeout(&group_waiting_sync_evt, 500) != NO_ERROR)
return __LINE__;
st = port_close(w_test_port1);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port1);
if (st < 0)
return __LINE__;
return 0;
}
#define RUN_TEST(t) result = t(); if (result) goto fail
int port_tests(void)
{
int result;
int count = 3;
while (count--) {
RUN_TEST(single_thread_basic);
RUN_TEST(single_thread_wait);
RUN_TEST(two_threads_basic);
RUN_TEST(group_basic);
RUN_TEST(group_dynamic);
}
printf("all tests passed\n");
return 0;
fail:
printf("test failed at line %d\n", result);
return 1;
}
#undef RUN_TEST
+122
Ver Arquivo
@@ -0,0 +1,122 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <stdio.h>
#include <string.h>
#include <debug.h>
#if !WITH_NO_FP
#include "float_test_vec.c"
static void printf_tests_float(void)
{
printf("floating point printf tests\n");
for (size_t i = 0; i < float_test_vec_size; i++) {
PRINT_FLOAT;
}
}
#endif
void printf_tests(void)
{
printf("printf tests\n");
printf("numbers:\n");
printf("int8: %hhd %hhd %hhd\n", -12, 0, 254);
printf("uint8: %hhu %hhu %hhu\n", -12, 0, 254);
printf("int16: %hd %hd %hd\n", -1234, 0, 1234);
printf("uint16:%hu %hu %hu\n", -1234, 0, 1234);
printf("int: %d %d %d\n", -12345678, 0, 12345678);
printf("uint: %u %u %u\n", -12345678, 0, 12345678);
printf("long: %ld %ld %ld\n", -12345678L, 0L, 12345678L);
printf("ulong: %lu %lu %lu\n", -12345678UL, 0UL, 12345678UL);
printf("longlong: %lli %lli %lli\n", -12345678LL, 0LL, 12345678LL);
printf("ulonglong: %llu %llu %llu\n", -12345678LL, 0LL, 12345678LL);
printf("ssize_t: %zd %zd %zd\n", (ssize_t)-12345678, (ssize_t)0, (ssize_t)12345678);
printf("usize_t: %zu %zu %zu\n", (size_t)-12345678, (size_t)0, (size_t)12345678);
printf("intmax_t: %jd %jd %jd\n", (intmax_t)-12345678, (intmax_t)0, (intmax_t)12345678);
printf("uintmax_t: %ju %ju %ju\n", (uintmax_t)-12345678, (uintmax_t)0, (uintmax_t)12345678);
printf("ptrdiff_t: %td %td %td\n", (ptrdiff_t)-12345678, (ptrdiff_t)0, (ptrdiff_t)12345678);
printf("ptrdiff_t (u): %tu %tu %tu\n", (ptrdiff_t)-12345678, (ptrdiff_t)0, (ptrdiff_t)12345678);
printf("hex:\n");
printf("uint8: %hhx %hhx %hhx\n", -12, 0, 254);
printf("uint16:%hx %hx %hx\n", -1234, 0, 1234);
printf("uint: %x %x %x\n", -12345678, 0, 12345678);
printf("ulong: %lx %lx %lx\n", -12345678UL, 0UL, 12345678UL);
printf("ulong: %X %X %X\n", -12345678, 0, 12345678);
printf("ulonglong: %llx %llx %llx\n", -12345678LL, 0LL, 12345678LL);
printf("usize_t: %zx %zx %zx\n", (size_t)-12345678, (size_t)0, (size_t)12345678);
printf("alt/sign:\n");
printf("uint: %#x %#X\n", 0xabcdef, 0xabcdef);
printf("int: %+d %+d\n", 12345678, -12345678);
printf("int: % d %+d\n", 12345678, 12345678);
printf("formatting\n");
printf("int: a%8da\n", 12345678);
printf("int: a%9da\n", 12345678);
printf("int: a%-9da\n", 12345678);
printf("int: a%10da\n", 12345678);
printf("int: a%-10da\n", 12345678);
printf("int: a%09da\n", 12345678);
printf("int: a%010da\n", 12345678);
printf("int: a%6da\n", 12345678);
printf("a%1sa\n", "b");
printf("a%9sa\n", "b");
printf("a%-9sa\n", "b");
printf("a%5sa\n", "thisisatest");
printf("%03d\n", -2); /* '-02' */
printf("%0+3d\n", -2); /* '-02' */
printf("%0+3d\n", 2); /* '+02' */
printf("%+3d\n", 2); /* ' +2' */
printf("% 3d\n", -2000); /* '-2000' */
printf("% 3d\n", 2000); /* ' 2000' */
printf("%+3d\n", 2000); /* '+2000' */
printf("%10s\n", "test"); /* ' test' */
printf("%010s\n", "test"); /* ' test' */
printf("%-10s\n", "test"); /* 'test ' */
printf("%-010s\n", "test"); /* 'test ' */
int err;
err = printf("a");
printf(" returned %d\n", err);
err = printf("ab");
printf(" returned %d\n", err);
err = printf("abc");
printf(" returned %d\n", err);
err = printf("abcd");
printf(" returned %d\n", err);
err = printf("abcde");
printf(" returned %d\n", err);
err = printf("abcdef");
printf(" returned %d\n", err);
/* make sure snprintf terminates at the right spot */
char buf[32];
memset(buf, 0, sizeof(buf));
err = sprintf(buf, "0123456789abcdef012345678");
printf("sprintf returns %d\n", err);
hexdump8(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
err = snprintf(buf, 15, "0123456789abcdef012345678");
printf("snprintf returns %d\n", err);
hexdump8(buf, sizeof(buf));
#if !WITH_NO_FP
printf_tests_float();
#endif
}
+16
Ver Arquivo
@@ -0,0 +1,16 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "ref_call_counter.h"
RefCallCounter::RefCallCounter() : add_ref_calls_(0u), release_calls_(0u) {}
void RefCallCounter::AddRef() { add_ref_calls_++; }
bool RefCallCounter::Release()
{
release_calls_++;
return false;
}
+24
Ver Arquivo
@@ -0,0 +1,24 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
class RefCallCounter {
public:
RefCallCounter();
void AddRef();
bool Release();
void Adopt() {}
int add_ref_calls() const { return add_ref_calls_; }
int release_calls() const { return release_calls_; }
private:
int add_ref_calls_;
int release_calls_;
};
+64
Ver Arquivo
@@ -0,0 +1,64 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <kernel/thread.h>
#include <unittest.h>
#include <utils/ref_counted.h>
#include <utils/ref_ptr.h>
class DestructionTracker : public utils::RefCounted<DestructionTracker> {
public:
explicit DestructionTracker(bool* destroyed) : destroyed_(destroyed) {}
~DestructionTracker() { *destroyed_ = true; }
private:
bool* destroyed_;
};
static int inc_and_dec(void* arg)
{
DestructionTracker* tracker = reinterpret_cast<DestructionTracker*>(arg);
for (size_t i = 0u; i < 500; ++i) {
utils::RefPtr<DestructionTracker> ptr(tracker);
}
return 0;
}
extern "C" int ref_counted_tests(int argc, const cmd_args* argv)
{
BEGIN_TEST;
bool destroyed = false;
{
utils::RefPtr<DestructionTracker> ptr = utils::AdoptRef(new DestructionTracker(&destroyed));
EXPECT_FALSE(destroyed, "should not be destroyed");
void* arg = reinterpret_cast<void*>(ptr.get());
thread_t* threads[5];
threads[0] = thread_create("inc_and_dec thread 0", &inc_and_dec, arg, DEFAULT_PRIORITY,
DEFAULT_STACK_SIZE);
threads[1] = thread_create("inc_and_dec thread 1", &inc_and_dec, arg, DEFAULT_PRIORITY,
DEFAULT_STACK_SIZE);
threads[2] = thread_create("inc_and_dec thread 2", &inc_and_dec, arg, DEFAULT_PRIORITY,
DEFAULT_STACK_SIZE);
threads[3] = thread_create("inc_and_dec thread 3", &inc_and_dec, arg, DEFAULT_PRIORITY,
DEFAULT_STACK_SIZE);
threads[4] = thread_create("inc_and_dec thread 4", &inc_and_dec, arg, DEFAULT_PRIORITY,
DEFAULT_STACK_SIZE);
for (size_t i = 0u; i < 5u; ++i) thread_resume(threads[i]);
inc_and_dec(arg);
for (size_t i = 0u; i < 5u; ++i) thread_join(threads[i], NULL, INFINITE_TIME);
EXPECT_FALSE(destroyed, "should not be destroyed after inc/dec pairs");
}
EXPECT_TRUE(destroyed, "should be when RefPtr falls out of scope");
printf("all tests done\n");
END_TEST;
}
+83
Ver Arquivo
@@ -0,0 +1,83 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <stdio.h>
#include <unittest.h>
#include <utils/ref_ptr.h>
#include "ref_call_counter.h"
extern "C" int ref_ptr_tests(int argc, const cmd_args* argv)
{
BEGIN_TEST;
using RefCallPtr = utils::RefPtr<RefCallCounter>;
RefCallCounter counter;
RefCallPtr ptr = utils::AdoptRef<RefCallCounter>(&counter);
EXPECT_TRUE(&counter == ptr.get(), ".get() should point to object");
EXPECT_TRUE(static_cast<bool>(ptr), "operator bool");
EXPECT_TRUE(&counter == &(*ptr), "operator*");
// Adoption should not manipulate the refcount.
EXPECT_EQ(0, counter.add_ref_calls(), "");
EXPECT_EQ(0, counter.release_calls(), "");
{
RefCallPtr ptr2 = ptr;
// Copying to a new RefPtr should call add once.
EXPECT_EQ(1, counter.add_ref_calls(), "");
EXPECT_EQ(0, counter.release_calls(), "");
}
// Destroying the new RefPtr should release once.
EXPECT_EQ(1, counter.add_ref_calls(), "");
EXPECT_EQ(1, counter.release_calls(), "");
{
RefCallPtr ptr2;
EXPECT_TRUE(!static_cast<bool>(ptr2), "");
ptr.swap(ptr2);
// Swapping shouldn't cause any add or release calls, but should update
// values.
EXPECT_EQ(1, counter.add_ref_calls(), "");
EXPECT_EQ(1, counter.release_calls(), "");
EXPECT_TRUE(!static_cast<bool>(ptr), "");
EXPECT_TRUE(&counter == ptr2.get(), "");
ptr2.swap(ptr);
}
EXPECT_EQ(1, counter.add_ref_calls(), "");
EXPECT_EQ(1, counter.release_calls(), "");
{
RefCallPtr ptr2 = utils::move(ptr);
// Moving shouldn't cause any add or release but should update values.
EXPECT_EQ(1, counter.add_ref_calls(), "");
EXPECT_EQ(1, counter.release_calls(), "");
EXPECT_FALSE(static_cast<bool>(ptr), "");
EXPECT_TRUE(&counter == ptr2.get(), "");
ptr2.swap(ptr);
}
// Reset should calls release and clear out the pointer.
ptr.reset(nullptr);
EXPECT_EQ(2, counter.release_calls(), "");
EXPECT_FALSE(static_cast<bool>(ptr), "");
EXPECT_FALSE(ptr.get(), "");
printf("all tests done\n");
END_TEST;
}
+48
Ver Arquivo
@@ -0,0 +1,48 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/arena_tests.cpp \
$(LOCAL_DIR)/auto_call_tests.cpp \
$(LOCAL_DIR)/benchmarks.c \
$(LOCAL_DIR)/cache_tests.c \
$(LOCAL_DIR)/clock_tests.c \
$(LOCAL_DIR)/fibo.c \
$(LOCAL_DIR)/float.c \
$(LOCAL_DIR)/float_instructions.S \
$(LOCAL_DIR)/float_test_vec.c \
$(LOCAL_DIR)/forward_tests.cpp \
$(LOCAL_DIR)/hash_tests.cpp \
$(LOCAL_DIR)/list_tests.cpp \
$(LOCAL_DIR)/mem_tests.c \
$(LOCAL_DIR)/port_tests.c \
$(LOCAL_DIR)/printf_tests.c \
$(LOCAL_DIR)/ref_call_counter.cpp \
$(LOCAL_DIR)/ref_counted_tests.cpp \
$(LOCAL_DIR)/ref_ptr_tests.cpp \
$(LOCAL_DIR)/sync_ipi_tests.c \
$(LOCAL_DIR)/tests.c \
$(LOCAL_DIR)/thread_tests.c \
$(LOCAL_DIR)/unique_ptr_tests.cpp \
$(LOCAL_DIR)/unittests.c \
$(LOCAL_DIR)/vm_tests.cpp \
MODULE_DEPS += \
lib/safeint \
lib/unittest \
lib/utils \
lib/crypto \
MODULE_ARM_OVERRIDE_SRCS := \
MODULE_COMPILEFLAGS += -Wno-format -fno-builtin
include make/module.mk
+78
Ver Arquivo
@@ -0,0 +1,78 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <trace.h>
#include <app/tests.h>
#include <arch/ops.h>
#include <kernel/mp.h>
#define LOCAL_TRACE 0
#define TEST_RUNS 1000
void inorder_count_task(void *raw_context) {
ASSERT(arch_ints_disabled());
int *inorder_counter = raw_context;
uint cpu_num = arch_curr_cpu_num();
int oldval = atomic_add(inorder_counter, 1);
ASSERT(oldval == (int)cpu_num);
LTRACEF(" CPU %u checked in\n", cpu_num);
}
void counter_task(void *raw_context) {
ASSERT(arch_ints_disabled());
int *counter = raw_context;
atomic_add(counter, 1);
}
int sync_ipi_tests(int argc, const cmd_args *argv)
{
uint num_cpus = arch_max_num_cpus();
uint online = mp_get_online_mask();
if (online != (1U << num_cpus) - 1) {
printf("Can only run test with all CPUs online\n");
return ERR_NOT_SUPPORTED;
}
uint runs = TEST_RUNS;
if (argc > 1) {
runs = argv[1].u;
}
/* Test that we're actually blocking and only signaling the ones we target */
for (uint i = 0; i < runs; ++i) {
LTRACEF("Sequential test\n");
int inorder_counter = 0;
for (uint i = 0; i < num_cpus; ++i) {
mp_sync_exec(1 << i, inorder_count_task, &inorder_counter);
LTRACEF(" Finished signaling CPU %u\n", i);
}
}
/* Test that we can signal multiple CPUs at the same time */
for (uint i = 0; i < runs; ++i) {
LTRACEF("Counter test (%u CPUs)\n", num_cpus);
int counter = 0;
spin_lock_saved_state_t irqstate;
arch_interrupt_save(&irqstate, SPIN_LOCK_FLAG_INTERRUPTS);
mp_sync_exec(MP_CPU_ALL_BUT_LOCAL, counter_task, &counter);
arch_interrupt_restore(irqstate, SPIN_LOCK_FLAG_INTERRUPTS);
LTRACEF(" Finished signaling all but local (%d)\n", counter);
ASSERT((uint)counter == num_cpus - 1);
}
printf("Success\n");
return NO_ERROR;
}
+47
Ver Arquivo
@@ -0,0 +1,47 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app.h>
#include <debug.h>
#include <app/tests.h>
#include <compiler.h>
#if defined(WITH_LIB_CONSOLE)
#include <lib/console.h>
STATIC_COMMAND_START
STATIC_COMMAND("printf_tests", "test printf", (console_cmd)&printf_tests)
STATIC_COMMAND("thread_tests", "test the scheduler", (console_cmd)&thread_tests)
STATIC_COMMAND("port_tests", "test the ports", (console_cmd)&port_tests)
STATIC_COMMAND("clock_tests", "test clocks", (console_cmd)&clock_tests)
STATIC_COMMAND("bench", "miscellaneous benchmarks", (console_cmd)&benchmarks)
STATIC_COMMAND("fibo", "threaded fibonacci", (console_cmd)&fibo)
STATIC_COMMAND("spinner", "create a spinning thread", (console_cmd)&spinner)
STATIC_COMMAND("ref_ptr_tests", "test ref_ptr", (console_cmd)&ref_ptr_tests)
STATIC_COMMAND("ref_counted_tests", "test ref_counted", (console_cmd)&ref_counted_tests)
STATIC_COMMAND("unique_ptr_tests", "test unique_ptr", (console_cmd)&unique_ptr_tests)
STATIC_COMMAND("forward_tests", "test forward", (console_cmd)&forward_tests)
STATIC_COMMAND("list_tests", "test lists", (console_cmd)&list_tests)
STATIC_COMMAND("hash_tests", "test hash tables", (console_cmd)&hash_tests)
STATIC_COMMAND("vm_tests", "test the virtual memory system", (console_cmd)&vm_tests)
STATIC_COMMAND("auto_call_tests", "test auto call", (console_cmd)&auto_call_tests)
STATIC_COMMAND("sync_ipi_tests", "test synchronous IPIs", (console_cmd)&sync_ipi_tests)
STATIC_COMMAND("arena_tests", "test arena allocator", (console_cmd)&arena_tests)
STATIC_COMMAND("unit_tests", "run all unittests", (console_cmd)&unittests)
STATIC_COMMAND_END(tests);
#endif
static void tests_init(const struct app_descriptor *app)
{
}
APP_START(tests)
.init = tests_init,
.flags = 0,
APP_END
+649
Ver Arquivo
@@ -0,0 +1,649 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <trace.h>
#include <rand.h>
#include <err.h>
#include <assert.h>
#include <string.h>
#include <app/tests.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include <kernel/semaphore.h>
#include <kernel/event.h>
#include <platform.h>
static int sleep_thread(void *arg)
{
for (;;) {
printf("sleeper %p\n", get_current_thread());
thread_sleep(rand() % 500);
}
return 0;
}
int sleep_test(void)
{
int i;
for (i=0; i < 16; i++)
thread_detach_and_resume(thread_create("sleeper", &sleep_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
return 0;
}
static semaphore_t sem;
static const int sem_total_its = 10000;
static const int sem_thread_max_its = 1000;
static const int sem_start_value = 10;
static int sem_remaining_its = 0;
static int sem_threads = 0;
static mutex_t sem_test_mutex;
static int semaphore_producer(void *unused)
{
printf("semaphore producer %p starting up, running for %d iterations\n", get_current_thread(), sem_total_its);
for (int x = 0; x < sem_total_its; x++) {
sem_post(&sem, true);
}
return 0;
}
static int semaphore_consumer(void *unused)
{
unsigned int iterations = 0;
mutex_acquire(&sem_test_mutex);
if (sem_remaining_its >= sem_thread_max_its) {
iterations = rand();
iterations %= sem_thread_max_its;
} else {
iterations = sem_remaining_its;
}
sem_remaining_its -= iterations;
mutex_release(&sem_test_mutex);
printf("semaphore consumer %p starting up, running for %u iterations\n", get_current_thread(), iterations);
for (unsigned int x = 0; x < iterations; x++)
sem_wait(&sem);
printf("semaphore consumer %p done\n", get_current_thread());
atomic_add(&sem_threads, -1);
return 0;
}
static int semaphore_test(void)
{
static semaphore_t isem = SEMAPHORE_INITIAL_VALUE(isem, 99);
printf("preinitialized sempahore:\n");
hexdump(&isem, sizeof(isem));
sem_init(&sem, sem_start_value);
mutex_init(&sem_test_mutex);
sem_remaining_its = sem_total_its;
while (1) {
mutex_acquire(&sem_test_mutex);
if (sem_remaining_its) {
thread_detach_and_resume(thread_create("semaphore consumer", &semaphore_consumer, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
atomic_add(&sem_threads, 1);
} else {
mutex_release(&sem_test_mutex);
break;
}
mutex_release(&sem_test_mutex);
}
thread_detach_and_resume(thread_create("semaphore producer", &semaphore_producer, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
while (sem_threads)
thread_yield();
if (sem.count == sem_start_value)
printf("semaphore tests successfully complete\n");
else
printf("semaphore tests failed: %d != %d\n", sem.count, sem_start_value);
sem_destroy(&sem);
mutex_destroy(&sem_test_mutex);
return 0;
}
static int mutex_thread(void *arg)
{
int i;
const int iterations = 1000000;
static volatile int shared = 0;
mutex_t *m = (mutex_t *)arg;
printf("mutex tester thread %p starting up, will go for %d iterations\n", get_current_thread(), iterations);
for (i = 0; i < iterations; i++) {
mutex_acquire(m);
if (shared != 0)
panic("someone else has messed with the shared data\n");
shared = (intptr_t)get_current_thread();
thread_yield();
shared = 0;
mutex_release(m);
thread_yield();
}
return 0;
}
static int mutex_timeout_thread(void *arg)
{
mutex_t *timeout_mutex = (mutex_t *)arg;
status_t err;
printf("mutex_timeout_thread acquiring mutex %p with 1 second timeout\n", timeout_mutex);
err = mutex_acquire_timeout(timeout_mutex, 1000);
if (err == ERR_TIMED_OUT)
printf("mutex_acquire_timeout returns with TIMEOUT\n");
else
printf("mutex_acquire_timeout returns %d\n", err);
return err;
}
static int mutex_zerotimeout_thread(void *arg)
{
mutex_t *timeout_mutex = (mutex_t *)arg;
status_t err;
printf("mutex_zerotimeout_thread acquiring mutex %p with zero second timeout\n", timeout_mutex);
err = mutex_acquire_timeout(timeout_mutex, 0);
if (err == ERR_TIMED_OUT)
printf("mutex_acquire_timeout returns with TIMEOUT\n");
else
printf("mutex_acquire_timeout returns %d\n", err);
return err;
}
int mutex_test(void)
{
static mutex_t imutex = MUTEX_INITIAL_VALUE(imutex);
printf("preinitialized mutex:\n");
hexdump(&imutex, sizeof(imutex));
mutex_t m;
mutex_init(&m);
thread_t *threads[5];
for (uint i=0; i < countof(threads); i++) {
threads[i] = thread_create("mutex tester", &mutex_thread, &m, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(threads[i]);
}
for (uint i=0; i < countof(threads); i++) {
thread_join(threads[i], NULL, INFINITE_TIME);
}
printf("done with simple mutex tests\n");
printf("testing mutex timeout\n");
mutex_t timeout_mutex;
mutex_init(&timeout_mutex);
mutex_acquire(&timeout_mutex);
for (uint i=0; i < 2; i++) {
threads[i] = thread_create("mutex timeout tester", &mutex_timeout_thread, (void *)&timeout_mutex, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(threads[i]);
}
for (uint i=2; i < 4; i++) {
threads[i] = thread_create("mutex timeout tester", &mutex_zerotimeout_thread, (void *)&timeout_mutex, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(threads[i]);
}
thread_sleep(5000);
mutex_release(&timeout_mutex);
for (uint i=0; i < 4; i++) {
thread_join(threads[i], NULL, INFINITE_TIME);
}
printf("done with mutex tests\n");
mutex_destroy(&timeout_mutex);
return 0;
}
static event_t e;
static int event_signaller(void *arg)
{
printf("event signaller pausing\n");
thread_sleep(1000);
// for (;;) {
printf("signalling event\n");
event_signal(&e, true);
printf("done signalling event\n");
thread_yield();
// }
return 0;
}
static int event_waiter(void *arg)
{
int count = (intptr_t)arg;
printf("event waiter starting\n");
while (count > 0) {
printf("%p: waiting on event...\n", get_current_thread());
if (event_wait(&e) < 0) {
printf("%p: event_wait() returned error\n", get_current_thread());
return -1;
}
printf("%p: done waiting on event...\n", get_current_thread());
thread_yield();
count--;
}
return 0;
}
void event_test(void)
{
thread_t *threads[5];
static event_t ievent = EVENT_INITIAL_VALUE(ievent, true, 0x1234);
printf("preinitialized event:\n");
hexdump(&ievent, sizeof(ievent));
printf("event tests starting\n");
/* make sure signalling the event wakes up all the threads */
event_init(&e, false, 0);
threads[0] = thread_create("event signaller", &event_signaller, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[1] = thread_create("event waiter 0", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[2] = thread_create("event waiter 1", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[3] = thread_create("event waiter 2", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[4] = thread_create("event waiter 3", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
for (uint i = 0; i < countof(threads); i++)
thread_resume(threads[i]);
thread_sleep(2000);
printf("destroying event\n");
event_destroy(&e);
for (uint i = 0; i < countof(threads); i++)
thread_join(threads[i], NULL, INFINITE_TIME);
/* make sure signalling the event wakes up precisely one thread */
event_init(&e, false, EVENT_FLAG_AUTOUNSIGNAL);
threads[0] = thread_create("event signaller", &event_signaller, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[1] = thread_create("event waiter 0", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[2] = thread_create("event waiter 1", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[3] = thread_create("event waiter 2", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
threads[4] = thread_create("event waiter 3", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
for (uint i = 0; i < countof(threads); i++)
thread_resume(threads[i]);
thread_sleep(2000);
event_destroy(&e);
for (uint i = 0; i < countof(threads); i++)
thread_join(threads[i], NULL, INFINITE_TIME);
printf("event tests done\n");
}
static int quantum_tester(void *arg)
{
for (;;) {
printf("%p: in this thread. rq %d\n", get_current_thread(), get_current_thread()->remaining_quantum);
}
return 0;
}
void quantum_test(void)
{
thread_detach_and_resume(thread_create("quantum tester 0", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("quantum tester 1", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("quantum tester 2", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("quantum tester 3", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
static event_t context_switch_event;
static event_t context_switch_done_event;
static int context_switch_tester(void *arg)
{
int i;
uint total_count = 0;
const int iter = 100000;
int thread_count = (intptr_t)arg;
event_wait(&context_switch_event);
uint count = arch_cycle_count();
for (i = 0; i < iter; i++) {
thread_yield();
}
total_count += arch_cycle_count() - count;
thread_sleep(1000);
printf("took %u cycles to yield %d times, %u per yield, %u per yield per thread\n",
total_count, iter, total_count / iter, total_count / iter / thread_count);
event_signal(&context_switch_done_event, true);
return 0;
}
void context_switch_test(void)
{
event_init(&context_switch_event, false, 0);
event_init(&context_switch_done_event, false, 0);
thread_detach_and_resume(thread_create("context switch idle", &context_switch_tester, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_sleep(100);
event_signal(&context_switch_event, true);
event_wait(&context_switch_done_event);
thread_sleep(100);
event_unsignal(&context_switch_event);
event_unsignal(&context_switch_done_event);
thread_detach_and_resume(thread_create("context switch 2a", &context_switch_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("context switch 2b", &context_switch_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_sleep(100);
event_signal(&context_switch_event, true);
event_wait(&context_switch_done_event);
thread_sleep(100);
event_unsignal(&context_switch_event);
event_unsignal(&context_switch_done_event);
thread_detach_and_resume(thread_create("context switch 4a", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("context switch 4b", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("context switch 4c", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_detach_and_resume(thread_create("context switch 4d", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
thread_sleep(100);
event_signal(&context_switch_event, true);
event_wait(&context_switch_done_event);
thread_sleep(100);
}
static volatile int atomic;
static volatile int atomic_count;
static int atomic_tester(void *arg)
{
int add = (intptr_t)arg;
int i;
const int iter = 10000000;
TRACEF("add %d, %d iterations\n", add, iter);
for (i=0; i < iter; i++) {
atomic_add(&atomic, add);
}
int old = atomic_add(&atomic_count, -1);
TRACEF("exiting, old count %d\n", old);
return 0;
}
static void atomic_test(void)
{
atomic = 0;
atomic_count = 8;
printf("testing atomic routines\n");
thread_t *threads[8];
threads[0] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[1] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[2] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[3] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[4] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[5] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[6] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
threads[7] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
/* start all the threads */
for (uint i = 0; i < countof(threads); i++)
thread_resume(threads[i]);
/* wait for them to all stop */
for (uint i = 0; i < countof(threads); i++) {
thread_join(threads[i], NULL, INFINITE_TIME);
}
printf("atomic count == %d (should be zero)\n", atomic);
}
static volatile int preempt_count;
static int preempt_tester(void *arg)
{
spin(1000000);
printf("exiting ts %lld\n", current_time_hires());
atomic_add(&preempt_count, -1);
#undef COUNT
return 0;
}
static void preempt_test(void)
{
/* create 5 threads, let them run. If the system is properly timer preempting,
* the threads should interleave each other at a fine enough granularity so
* that they complete at roughly the same time. */
printf("testing preemption\n");
preempt_count = 5;
for (int i = 0; i < preempt_count; i++)
thread_detach_and_resume(thread_create("preempt tester", &preempt_tester, NULL, LOW_PRIORITY, DEFAULT_STACK_SIZE));
while (preempt_count > 0) {
thread_sleep(1000);
}
printf("done with preempt test, above time stamps should be very close\n");
/* do the same as above, but mark the threads as real time, which should
* effectively disable timer based preemption for them. They should
* complete in order, about a second apart. */
printf("testing real time preemption\n");
preempt_count = 5;
for (int i = 0; i < preempt_count; i++) {
thread_t *t = thread_create("preempt tester", &preempt_tester, NULL, LOW_PRIORITY, DEFAULT_STACK_SIZE);
thread_set_real_time(t);
thread_detach_and_resume(t);
}
while (preempt_count > 0) {
thread_sleep(1000);
}
printf("done with real-time preempt test, above time stamps should be 1 second apart\n");
}
static int join_tester(void *arg)
{
long val = (long)arg;
printf("\t\tjoin tester starting\n");
thread_sleep(500);
printf("\t\tjoin tester exiting with result %ld\n", val);
return val;
}
static int join_tester_server(void *arg)
{
int ret;
status_t err;
thread_t *t;
printf("\ttesting thread_join/thread_detach\n");
printf("\tcreating and waiting on thread to exit with thread_join\n");
t = thread_create("join tester", &join_tester, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t);
ret = 99;
printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
err = thread_join(t, &ret, INFINITE_TIME);
printf("\tthread_join returns err %d, retval %d\n", err, ret);
printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
printf("\tcreating and waiting on thread to exit with thread_join, after thread has exited\n");
t = thread_create("join tester", &join_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t);
thread_sleep(1000); // wait until thread is already dead
ret = 99;
printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
err = thread_join(t, &ret, INFINITE_TIME);
printf("\tthread_join returns err %d, retval %d\n", err, ret);
printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
printf("\tcreating a thread, detaching it, let it exit on its own\n");
t = thread_create("join tester", &join_tester, (void *)3, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_detach(t);
thread_resume(t);
thread_sleep(1000); // wait until the thread should be dead
printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
printf("\tcreating a thread, detaching it after it should be dead\n");
t = thread_create("join tester", &join_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t);
thread_sleep(1000); // wait until thread is already dead
printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
thread_detach(t);
printf("\tthread magic is 0x%x\n", t->magic);
printf("\texiting join tester server\n");
return 55;
}
static void join_test(void)
{
int ret;
status_t err;
thread_t *t;
printf("testing thread_join/thread_detach\n");
printf("creating thread join server thread\n");
t = thread_create("join tester server", &join_tester_server, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t);
ret = 99;
err = thread_join(t, &ret, INFINITE_TIME);
printf("thread_join returns err %d, retval %d (should be 0 and 55)\n", err, ret);
}
static void spinlock_test(void)
{
spin_lock_saved_state_t state;
spin_lock_t lock;
spin_lock_init(&lock);
// verify basic functionality (single core)
printf("testing spinlock:\n");
ASSERT(!spin_lock_held(&lock));
ASSERT(!arch_ints_disabled());
spin_lock_irqsave(&lock, state);
ASSERT(arch_ints_disabled());
ASSERT(spin_lock_held(&lock));
spin_unlock_irqrestore(&lock, state);
ASSERT(!spin_lock_held(&lock));
ASSERT(!arch_ints_disabled());
printf("seems to work\n");
#define COUNT (1024*1024)
uint32_t c = arch_cycle_count();
for (uint i = 0; i < COUNT; i++) {
spin_lock(&lock);
spin_unlock(&lock);
}
c = arch_cycle_count() - c;
printf("%u cycles to acquire/release lock %u times (%u cycles per)\n", c, COUNT, c / COUNT);
c = arch_cycle_count();
for (uint i = 0; i < COUNT; i++) {
spin_lock_irqsave(&lock, state);
spin_unlock_irqrestore(&lock, state);
}
c = arch_cycle_count() - c;
printf("%u cycles to acquire/release lock w/irqsave %u times (%u cycles per)\n", c, COUNT, c / COUNT);
#undef COUNT
}
int thread_tests(void)
{
mutex_test();
semaphore_test();
event_test();
spinlock_test();
atomic_test();
thread_sleep(200);
context_switch_test();
preempt_test();
join_test();
return 0;
}
static int spinner_thread(void *arg)
{
for (;;)
;
return 0;
}
int spinner(int argc, const cmd_args *argv)
{
if (argc < 2) {
printf("not enough args\n");
printf("usage: %s <priority> <rt>\n", argv[0].str);
return -1;
}
thread_t *t = thread_create("spinner", spinner_thread, NULL, argv[1].u, DEFAULT_STACK_SIZE);
if (!t)
return ERR_NO_MEMORY;
if (argc >= 3 && !strcmp(argv[2].str, "rt")) {
thread_set_real_time(t);
}
thread_resume(t);
return 0;
}
+151
Ver Arquivo
@@ -0,0 +1,151 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <stdio.h>
#include <unittest.h>
#include <utils/unique_ptr.h>
static int destroy_count = 0;
struct CountingDeleter {
void operator()(int* p)
{
destroy_count++;
delete p;
}
};
extern "C" int unique_ptr_tests(int argc, const cmd_args* argv)
{
BEGIN_TEST;
// Test for unique_ptr<T, ...> variant
using CountingPtr = utils::unique_ptr<int, CountingDeleter>;
// Construct and let a unique_ptr fall out of scope.
{
CountingPtr ptr(new int);
}
EXPECT_EQ(1, destroy_count, "");
destroy_count = 0;
// Construct and move into another unique_ptr.
{
CountingPtr ptr(new int);
CountingPtr ptr2 = utils::move(ptr);
if (ptr.get() != nullptr) {
printf("assert failed: expected ptr to be null on line %d\n", __LINE__);
return 1;
}
}
EXPECT_EQ(1, destroy_count, "");
destroy_count = 0;
// Construct a null unique_ptr and let it fall out of scope - should not call
// deleter.
{
CountingPtr ptr(nullptr);
}
EXPECT_EQ(0, destroy_count, "");
// Construct a pair of unique_ptrs in different scopes, swap them, and verify
// that the values change places and that the values are destroyed at the
// correct times.
{
CountingPtr ptr1(new int(4));
{
CountingPtr ptr2(new int(7));
ptr1.swap(ptr2);
EXPECT_EQ(7, *ptr1, "");
EXPECT_EQ(4, *ptr2, "");
}
EXPECT_EQ(1, destroy_count, "");
}
EXPECT_EQ(2, destroy_count, "");
destroy_count = 0;
// Test operator bool
{
CountingPtr foo(new int);
EXPECT_TRUE(static_cast<bool>(foo), "");
foo.reset();
EXPECT_EQ(1, destroy_count, "");
EXPECT_FALSE(static_cast<bool>(foo), "");
}
destroy_count = 0;
// Test for unique_ptr<T[], ...> variant
using CountingArrPtr = utils::unique_ptr<int[], CountingDeleter>;
// Construct and let a unique_ptr fall out of scope.
{
CountingArrPtr ptr(new int[1]);
}
EXPECT_EQ(1, destroy_count, "");
destroy_count = 0;
// Construct and move into another unique_ptr.
{
CountingArrPtr ptr(new int[1]);
CountingArrPtr ptr2 = utils::move(ptr);
}
EXPECT_EQ(1, destroy_count, "");
destroy_count = 0;
// Construct a null unique_ptr and let it fall out of scope - should not call
// deleter.
{
CountingArrPtr ptr(nullptr);
}
EXPECT_EQ(0, destroy_count, "");
// Construct a pair of unique_ptrs in different scopes, swap them, and verify
// that the values change places and that the values are destroyed at the
// correct times.
{
CountingArrPtr ptr1(new int[1]);
ptr1[0] = 4;
{
CountingArrPtr ptr2(new int[1]);
ptr2[0] = 7;
ptr1.swap(ptr2);
EXPECT_EQ(7, ptr1[0], "");
EXPECT_EQ(4, ptr2[0], "");
}
EXPECT_EQ(1, destroy_count, "");
}
EXPECT_EQ(2, destroy_count, "");
destroy_count = 0;
// Test operator bool
{
CountingArrPtr foo(new int[1]);
EXPECT_TRUE(static_cast<bool>(foo), "");
foo.reset();
EXPECT_EQ(1, destroy_count, "");
EXPECT_FALSE(static_cast<bool>(foo), "");
}
destroy_count = 0;
printf("all tests passed\n");
END_TEST;
}
+15
Ver Arquivo
@@ -0,0 +1,15 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2013, Google, Inc. All rights reserved
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <unittest.h>
void unittests()
{
bool success = run_all_tests();
printf(success ? "Success\n" : "Failure\n");
}
+555
Ver Arquivo
@@ -0,0 +1,555 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app/tests.h>
#include <assert.h>
#include <err.h>
#include <kernel/vm.h>
#include <kernel/vm/vm_aspace.h>
#include <kernel/vm/vm_object.h>
#include <kernel/vm/vm_region.h>
#include <unittest.h>
#include <utils/array.h>
__NO_INLINE static bool pmm_tests() {
BEGIN_TEST;
printf("starting pmm tests\n");
// allocate a single page, translate it to a vm_page_t and free it
printf("allocating single page, then freeing it\n");
{
paddr_t pa;
vm_page_t* page = pmm_alloc_page(0, &pa);
EXPECT_NEQ(nullptr, page, "pmm_alloc single page");
EXPECT_NEQ(0u, pa, "pmm_alloc single page");
vm_page_t* page2 = paddr_to_vm_page(pa);
EXPECT_EQ(page2, page, "paddr_to_vm_page on single page");
auto ret = pmm_free_page(page);
EXPECT_EQ(1u, ret, "pmm_free_page on single page");
}
// allocate a bunch of pages then free them
printf("allocating a lot of pages, then freeing them\n");
{
list_node list = LIST_INITIAL_VALUE(list);
static const size_t alloc_count = 1024;
auto count = pmm_alloc_pages(alloc_count, 0, &list);
EXPECT_EQ(alloc_count, count, "pmm_alloc_pages a bunch of pages count");
EXPECT_EQ(alloc_count, list_length(&list), "pmm_alloc_pages a bunch of pages list count");
auto ret = pmm_free(&list);
EXPECT_EQ(alloc_count, ret, "pmm_free_page on a list of pages");
}
// allocate too many pages and make sure it fails nicely
printf("allocating too many pages, then freeing them\n");
{
list_node list = LIST_INITIAL_VALUE(list);
static const size_t alloc_count = (128 * 1024 * 1024 * 1024ULL) / PAGE_SIZE; // 128GB
auto count = pmm_alloc_pages(alloc_count, 0, &list);
EXPECT_NEQ(alloc_count, 0, "pmm_alloc_pages too many pages count > 0");
EXPECT_NEQ(alloc_count, count, "pmm_alloc_pages too many pages count");
EXPECT_EQ(count, list_length(&list), "pmm_alloc_pages too many pages list count");
auto ret = pmm_free(&list);
EXPECT_EQ(count, ret, "pmm_free_page on a list of pages");
}
printf("done with pmm tests\n");
END_TEST;
}
static uint32_t test_rand(uint32_t seed) {
return (seed = seed * 1664525 + 1013904223);
}
// fill a region of memory with a pattern based on the address of the region
static void fill_region(uintptr_t seed, void* _ptr, size_t len) {
uint32_t* ptr = (uint32_t*)_ptr;
ASSERT(IS_ALIGNED((uintptr_t)ptr, 4));
uint32_t val = (uint32_t)seed;
#if UINTPTR_MAX > UINT32_MAX
val ^= (uint32_t)(seed >> 32);
#endif
for (size_t i = 0; i < len / 4; i++) {
ptr[i] = val;
val = test_rand(val);
}
}
// test a region of memory against a known pattern
static bool test_region(uintptr_t seed, void* _ptr, size_t len) {
uint32_t* ptr = (uint32_t*)_ptr;
ASSERT(IS_ALIGNED((uintptr_t)ptr, 4));
uint32_t val = (uint32_t)seed;
#if UINTPTR_MAX > UINT32_MAX
val ^= (uint32_t)(seed >> 32);
#endif
for (size_t i = 0; i < len / 4; i++) {
if (ptr[i] != val) {
printf("value at %p (%zu) is incorrect: 0x%x vs 0x%x\n", &ptr[i], i, ptr[i], val);
return false;
}
val = test_rand(val);
}
return true;
}
static void fill_and_test(bool& all_ok, void* ptr, size_t len) {
// fill it with a pattern
fill_region((uintptr_t)ptr, ptr, len);
// test that the pattern is read back properly
auto result = test_region((uintptr_t)ptr, ptr, len);
EXPECT_TRUE(result, "testing region for corruption");
}
__NO_INLINE static bool vmm_tests() {
BEGIN_TEST;
printf("starting vmm tests\n");
printf("allocating a region in kernel space, read/write it, then destroy it\n");
{
static const size_t alloc_size = 256 * 1024;
// allocate a region of memory
void* ptr;
auto err = vmm_alloc(vmm_get_kernel_aspace(), "test", alloc_size, &ptr, 0, 0, 0);
EXPECT_EQ(0, err, "vmm_allocate region of memory");
EXPECT_NEQ(nullptr, ptr, "vmm_allocate region of memory");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
// free the region
err = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)ptr);
EXPECT_EQ(0, err, "vmm_free_region region of memory");
}
printf("allocating a contiguous region in kernel space, read/write it, then destroy it\n");
{
static const size_t alloc_size = 256 * 1024;
// allocate a region of memory
void* ptr;
auto err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "test", alloc_size, &ptr, 0,
VMM_FLAG_COMMIT, 0);
EXPECT_EQ(0, err, "vmm_allocate_contiguous region of memory");
EXPECT_NEQ(nullptr, ptr, "vmm_allocate_contiguous region of memory");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
// test that it is indeed contiguous
printf("testing that region is contiguous\n");
paddr_t last_pa = 0;
for (size_t i = 0; i < alloc_size / PAGE_SIZE; i++) {
paddr_t pa = vaddr_to_paddr((uint8_t*)ptr + i * PAGE_SIZE);
if (last_pa != 0) {
EXPECT_EQ(pa, last_pa + PAGE_SIZE, "region is contiguous");
}
last_pa = pa;
}
// free the region
err = vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)ptr);
EXPECT_EQ(0, err, "vmm_free_region region of memory");
}
printf("allocating a new address space and creating a few regions in it, then destroy it\n");
{
void* ptr;
vmm_aspace_t* aspace;
static const size_t alloc_size = 16 * 1024;
auto err = vmm_create_aspace(&aspace, "test aspace", 0);
EXPECT_EQ(0, err, "vmm_allocate_aspace error code");
EXPECT_NEQ(nullptr, aspace, "vmm_allocate_aspace pointer");
vmm_aspace_t *old_aspace = get_current_thread()->aspace;
vmm_set_active_aspace(aspace);
// allocate region 0
err = vmm_alloc(aspace, "test0", alloc_size, &ptr, 0, 0, 0);
EXPECT_EQ(0, err, "vmm_allocate region of memory");
EXPECT_NEQ(nullptr, ptr, "vmm_allocate region of memory");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
// allocate region 1
err = vmm_alloc(aspace, "test1", 16384, &ptr, 0, 0, 0);
EXPECT_EQ(0, err, "vmm_allocate region of memory");
EXPECT_NEQ(nullptr, ptr, "vmm_allocate region of memory");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
// allocate region 2
err = vmm_alloc(aspace, "test2", 16384, &ptr, 0, 0, 0);
EXPECT_EQ(0, err, "vmm_allocate region of memory");
EXPECT_NEQ(nullptr, ptr, "vmm_allocate region of memory");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
vmm_set_active_aspace(old_aspace);
// free the address space all at once
err = vmm_free_aspace(aspace);
EXPECT_EQ(0, err, "vmm_free_aspace");
}
printf("test for some invalid arguments\n");
{
void* ptr;
status_t err;
// zero size
err = vmm_alloc(vmm_get_kernel_aspace(), "test", 0, &ptr, 0, 0, 0);
EXPECT_EQ(ERR_INVALID_ARGS, err, "invalid args to vmm_alloc");
// bad pointer
ptr = (void*)1;
err = vmm_alloc(vmm_get_kernel_aspace(), "test", 16384, &ptr, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_COMMIT, 0);
EXPECT_EQ(ERR_INVALID_ARGS, err, "invalid args to vmm_alloc");
// should have VMM_FLAG_COMMIT
err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "test", 4096, &ptr, 0, 0, 0);
EXPECT_EQ(ERR_INVALID_ARGS, err, "invalid args to vmm_alloc_contiguous");
// zero size
err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "test", 0, &ptr, 0, VMM_FLAG_COMMIT, 0);
EXPECT_EQ(ERR_INVALID_ARGS, err, "invalid args to vmm_alloc_contiguous");
}
printf("allocating a vm address space object directly, allowing it to go out of scope\n");
{
auto aspace = VmAspace::Create(0, "test aspace");
}
printf("allocating a vm address space object directly, mapping somethign on it, allowing it to go out of scope\n");
{
auto aspace = VmAspace::Create(0, "test aspace2");
void *ptr;
auto err = aspace->Alloc("test", PAGE_SIZE, &ptr, 0, 0, 0);
EXPECT_EQ(NO_ERROR, err, "allocating region\n");
// destroy the aspace, which should drop all the internal refs to it
aspace->Destroy();
// drop the ref held by this pointer
aspace.reset();
}
printf("verify there are no test aspaces left around\n");
DumpAllAspaces();
printf("done with vmm tests\n");
END_TEST;
}
__NO_INLINE static bool vmm_object_tests() {
BEGIN_TEST;
printf("starting vmm object based tests\n");
printf("creating vm object\n");
{
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, PAGE_SIZE);
EXPECT_TRUE(vmo, "vmobject creation\n");
}
printf("creating vm object, committing memory\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ret = vmo->CommitRange(0, alloc_size);
EXPECT_EQ((ssize_t)alloc_size, ret, "committing vm object\n");
}
printf("creating vm object, committing odd sized memory\n");
{
static const size_t alloc_size = 15;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ret = vmo->CommitRange(0, alloc_size);
EXPECT_EQ((ssize_t)alloc_size, ret, "committing vm object\n");
}
printf("creating vm object, committing contiguous memory\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ret = vmo->CommitRangeContiguous(0, alloc_size, 0);
EXPECT_EQ((ssize_t)alloc_size, ret, "committing vm object contiguously\n");
}
printf("creating vm object, mapping it, precommitted\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ka = VmAspace::kernel_aspace();
void* ptr;
auto ret =
ka->MapObject(vmo, "test", 0, alloc_size, &ptr, 0, VMM_FLAG_COMMIT, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(NO_ERROR, ret, "mapping object");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
auto err = ka->FreeRegion((vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, err, "unmapping object");
}
printf("creating vm object, mapping it, demand paged\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ka = VmAspace::kernel_aspace();
void* ptr;
auto ret = ka->MapObject(vmo, "test", 0, alloc_size, &ptr, 0, 0, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(ret, NO_ERROR, "mapping object");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
auto err = ka->FreeRegion((vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, err, "unmapping object");
}
printf("creating vm object, mapping it, dropping ref before unmapping\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ka = VmAspace::kernel_aspace();
void* ptr;
auto ret = ka->MapObject(utils::move(vmo), "test", 0, alloc_size, &ptr, 0, VMM_FLAG_COMMIT,
PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(ret, NO_ERROR, "mapping object");
EXPECT_FALSE(vmo, "dropped ref to object");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
auto err = ka->FreeRegion((vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, err, "unmapping object");
}
printf(
"creating vm object, mapping it, filling it with data, unmapping, mapping again somewhere "
"else\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ka = VmAspace::kernel_aspace();
void* ptr;
auto ret =
ka->MapObject(vmo, "test", 0, alloc_size, &ptr, 0, VMM_FLAG_COMMIT, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(NO_ERROR, ret, "mapping object");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
auto err = ka->FreeRegion((vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, err, "unmapping object");
// map it again
ret =
ka->MapObject(vmo, "test", 0, alloc_size, &ptr, 0, VMM_FLAG_COMMIT, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(ret, NO_ERROR, "mapping object");
// test that the pattern is still valid
bool result = test_region((uintptr_t)ptr, ptr, alloc_size);
EXPECT_TRUE(result, "testing region for corruption");
err = ka->FreeRegion((vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, err, "unmapping object");
}
printf(
"creating vm object, mapping it, filling it with data, mapping it a second time and third "
"time somwehere else\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
auto vmo = VmObject::Create(PMM_ALLOC_FLAG_ANY, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
auto ka = VmAspace::kernel_aspace();
void* ptr;
auto ret = ka->MapObject(vmo, "test0", 0, alloc_size, &ptr, 0, 0, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(NO_ERROR, ret, "mapping object");
// fill with known pattern and test
fill_and_test(all_ok, ptr, alloc_size);
// map it again
void* ptr2;
ret = ka->MapObject(vmo, "test1", 0, alloc_size, &ptr2, 0, 0, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(ret, NO_ERROR, "mapping object second time");
EXPECT_NEQ(ptr, ptr2, "second mapping is different");
// test that the pattern is still valid
bool result = test_region((uintptr_t)ptr, ptr2, alloc_size);
EXPECT_TRUE(result, "testing region for corruption");
// map it a third time with an offset
void* ptr3;
static const size_t alloc_offset = PAGE_SIZE;
ret = ka->MapObject(vmo, "test2", alloc_offset, alloc_size - alloc_offset, &ptr3, 0, 0,
PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(ret, NO_ERROR, "mapping object third time");
EXPECT_NEQ(ptr3, ptr2, "third mapping is different");
EXPECT_NEQ(ptr3, ptr, "third mapping is different");
// test that the pattern is still valid
int mc = memcmp((uint8_t*)ptr + alloc_offset, ptr3, alloc_size - alloc_offset);
EXPECT_EQ(0, mc, "testing region for corruption");
ret = ka->FreeRegion((vaddr_t)ptr3);
EXPECT_EQ(NO_ERROR, ret, "unmapping object third time");
ret = ka->FreeRegion((vaddr_t)ptr2);
EXPECT_EQ(NO_ERROR, ret, "unmapping object second time");
ret = ka->FreeRegion((vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, ret, "unmapping object");
}
printf("creating vm object, writing to it\n");
{
static const size_t alloc_size = PAGE_SIZE * 16;
// create object
auto vmo = VmObject::Create(0, alloc_size);
EXPECT_TRUE(vmo, "vmobject creation\n");
// create test buffer
utils::Array<uint8_t> a(new uint8_t[alloc_size], alloc_size);
fill_region(99, a.get(), alloc_size);
// write to it, make sure it seems to work with valid args
size_t bytes_written = -1;
status_t err = vmo->Write(a.get(), 0, 0, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(0u, bytes_written, "writing to object");
bytes_written = -1;
err = vmo->Write(a.get(), 0, 37, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(37u, bytes_written, "writing to object");
bytes_written = -1;
err = vmo->Write(a.get(), 99, 37, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(37u, bytes_written, "writing to object");
// should trim the returned size
bytes_written = -1;
err = vmo->Write(a.get(), 0, alloc_size + 47, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(alloc_size - 0, bytes_written, "writing to object");
bytes_written = -1;
err = vmo->Write(a.get(), 31, alloc_size + 47, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(alloc_size - 31, bytes_written, "writing to object");
// should return an error because out of range
bytes_written = -1;
err = vmo->Write(a.get(), alloc_size + 99, 42, &bytes_written);
EXPECT_EQ(ERR_OUT_OF_RANGE, err, "writing to object");
EXPECT_EQ(0u, bytes_written, "writing to object");
// map the object
auto ka = VmAspace::kernel_aspace();
uint8_t* ptr;
err = ka->MapObject(vmo, "test", 0, alloc_size, (void **)&ptr, 0, 0, PMM_ALLOC_FLAG_ANY);
EXPECT_EQ(NO_ERROR, err, "mapping object");
// write to it at odd offsets
bytes_written = -1;
err = vmo->Write(a.get(), 31, 4197, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(4197u, bytes_written, "writing to object");
int cmpres = memcmp(ptr + 31, a.get(), 4197);
EXPECT_EQ(0, cmpres, "reading from object");
// write to it, filling the object completely
err = vmo->Write(a.get(), 0, alloc_size, &bytes_written);
EXPECT_EQ(NO_ERROR, err, "writing to object");
EXPECT_EQ(alloc_size, bytes_written, "writing to object");
// test that the data was actually written to it
bool result = test_region(99, ptr, alloc_size);
EXPECT_TRUE(result, "writing to object");
// unmap it
ka->FreeRegion((vaddr_t)ptr);
// test that we can read from it
printf("reading back from vm object\n");
utils::Array<uint8_t> b(new uint8_t[alloc_size], alloc_size);
size_t bytes_read = -1;
err = vmo->Read(b.get(), 0, alloc_size, &bytes_read);
EXPECT_EQ(NO_ERROR, err, "reading from object");
EXPECT_EQ(alloc_size, bytes_read, "reading from object");
// validate the buffer is valid
cmpres = memcmp(b.get(), a.get(), alloc_size);
EXPECT_EQ(0, cmpres, "reading from object");
// read from it at an offset
bytes_read = -1;
err = vmo->Read(b.get(), 31, 4197, &bytes_read);
EXPECT_EQ(NO_ERROR, err, "reading from object");
EXPECT_EQ(4197u, bytes_read, "reading from object");
cmpres = memcmp(b.get(), a.get() + 31, 4197);
EXPECT_EQ(0, cmpres, "reading from object");
}
printf("done with vmm object based tests\n");
END_TEST;
}
int vm_tests(int argc, const cmd_args* argv) {
pmm_tests();
vmm_tests();
vmm_object_tests();
return 0;
}
+16
Ver Arquivo
@@ -0,0 +1,16 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
module("usbtest") {
sources = [
"descriptor.c",
"usbtest.c",
]
deps = [
"//kernel/dev/usb/client",
"//kernel/lib/libc",
]
}
+114
Ver Arquivo
@@ -0,0 +1,114 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <err.h>
#include <debug.h>
#include <stdio.h>
#include <target.h>
#include <compiler.h>
#include <usb/client/usb.h>
#include <usb/client/usbc.h>
#include <hw/usb.h>
#define W(w) (w & 0xff), (w >> 8)
#define W3(w) (w & 0xff), ((w >> 8) & 0xff), ((w >> 16) & 0xff)
/* top level device descriptor */
static const uint8_t dev_descr[] = {
0x12, /* descriptor length */
DEVICE, /* Device Descriptor type */
W(0x0200), /* USB Version */
0xff, /* class */
0xff, /* subclass */
0xff, /* protocol */
64, /* max packet size, ept0 */
W(0x9999), /* vendor */
W(0x9999), /* product */
W(0x9999), /* release */
0x0, /* manufacturer string */
0x0, /* product string */
0x0, /* serialno string */
0x1, /* num configs */
};
/* high/low speed device qualifier */
static const uint8_t devqual_descr[] = {
0x0a, /* len */
DEVICE_QUALIFIER, /* Device Qualifier type */
W(0x0200), /* USB version */
0x00, /* class */
0x00, /* subclass */
0x00, /* protocol */
64, /* max packet size, ept0 */
0x01, /* num configs */
0x00 /* reserved */
};
static const uint8_t cfg_descr[] = {
0x09, /* Length of Cfg Descr */
CONFIGURATION, /* Type of Cfg Descr */
W(0x09), /* Total Length (incl ifc, ept) */
0x00, /* # Interfaces */
0x01, /* Cfg Value */
0x00, /* Cfg String */
0xc0, /* Attributes -- self powered */
250, /* Power Consumption - 500mA */
};
static const uchar langid[] = { 0x04, 0x03, 0x09, 0x04 };
static const uint8_t if_descriptor_lowspeed[] = {
0x09, /* length */
INTERFACE, /* type */
0x01, /* interface num */
0x00, /* alternates */
0x02, /* endpoint count */
0xff, /* interface class */
0xff, /* interface subclass */
0x00, /* interface protocol */
0x00, /* string index */
/* endpoint 1 IN */
0x07, /* length */
ENDPOINT, /* type */
0x81, /* address: 1 IN */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
/* endpoint 1 OUT */
0x07, /* length */
ENDPOINT, /* type */
0x01, /* address: 1 OUT */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
};
static usb_config config = {
.lowspeed = {
.device = USB_DESC_STATIC(dev_descr),
.device_qual = USB_DESC_STATIC(devqual_descr),
.config = USB_DESC_STATIC(cfg_descr),
},
.highspeed = {
.device = USB_DESC_STATIC(dev_descr),
.device_qual = USB_DESC_STATIC(devqual_descr),
.config = USB_DESC_STATIC(cfg_descr),
},
.langid = USB_DESC_STATIC(langid),
};
void usbtest_usb_setup(void)
{
usb_setup(&config);
printf("appending interfaces\n");
usb_append_interface_lowspeed(if_descriptor_lowspeed, sizeof(if_descriptor_lowspeed));
usb_append_interface_highspeed(if_descriptor_lowspeed, sizeof(if_descriptor_lowspeed));
usbc_setup_endpoint(1, USB_OUT, 64);
usbc_setup_endpoint(1, USB_IN, 64);
}
+20
Ver Arquivo
@@ -0,0 +1,20 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_DEPS += \
dev/usb/client
MODULE_SRCS += \
$(LOCAL_DIR)/usbtest.c \
$(LOCAL_DIR)/descriptor.c \
include make/module.mk
+136
Ver Arquivo
@@ -0,0 +1,136 @@
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <app.h>
#include <debug.h>
#include <err.h>
#include <string.h>
#include <stdlib.h>
#include <trace.h>
#include <usb/client/usb.h>
#include <usb/client/usbc.h>
#include <kernel/debug.h>
#include <kernel/thread.h>
#include <kernel/event.h>
#define LOCAL_TRACE 1
extern void usbtest_usb_setup(void);
static status_t rx_callback(ep_t endpoint, struct usbc_transfer *transfer);
static usbc_transfer_t rx;
static uint8_t rxbuf[4096];
static volatile bool rxqueued;
static status_t tx_callback(ep_t endpoint, struct usbc_transfer *transfer);
static usbc_transfer_t tx;
static uint8_t txbuf[4095];
static volatile bool txqueued;
static event_t testevent;
/* RX */
static void queue_rx_transfer(void)
{
rx.callback = rx_callback;
rx.result = 0;
rx.buf = rxbuf;
rx.buflen = sizeof(rxbuf);
rx.bufpos = 0;
rx.extra = NULL;
memset(rxbuf, 0x99, sizeof(rxbuf));
rxqueued = true;
usbc_queue_rx(1, &rx);
}
static status_t rx_callback(ep_t endpoint, struct usbc_transfer *transfer)
{
LTRACEF("ep %u, transfer %p\n", endpoint, transfer);
rxqueued = false;
event_signal(&testevent, false);
return NO_ERROR;
}
/* TX */
static void queue_tx_transfer(void)
{
tx.callback = tx_callback;
tx.result = 0;
tx.buf = txbuf;
tx.buflen = sizeof(txbuf);
tx.bufpos = 0;
tx.extra = NULL;
for (uint i = 0; i < sizeof(txbuf); i++)
txbuf[i] = i * 3;
txqueued = true;
usbc_queue_tx(1, &tx);
}
static status_t tx_callback(ep_t endpoint, struct usbc_transfer *transfer)
{
LTRACEF("ep %u, transfer %p\n", endpoint, transfer);
txqueued = false;
event_signal(&testevent, false);
return NO_ERROR;
}
static void usbtest_init(const struct app_descriptor *app)
{
LTRACE_ENTRY;
event_init(&testevent, false, EVENT_FLAG_AUTOUNSIGNAL);
usbtest_usb_setup();
LTRACE_EXIT;
}
static void usbtest_entry(const struct app_descriptor *app, void *args)
{
LTRACE_ENTRY;
TRACEF("starting usb stack\n");
usb_start();
// XXX get callback from stack
thread_sleep(2000);
TRACEF("queuing transfers\n");
queue_rx_transfer();
queue_tx_transfer();
while (event_wait(&testevent) == NO_ERROR) {
if (!rxqueued) {
/* dump the state of the transfer */
LTRACEF("rx transfer completed\n");
usbc_dump_transfer(&rx);
hexdump8(rx.buf, MIN(128, rx.bufpos));
queue_rx_transfer();
}
if (!txqueued) {
/* dump the state of the transfer */
LTRACEF("tx transfer completed\n");
usbc_dump_transfer(&tx);
queue_tx_transfer();
}
}
LTRACE_EXIT;
}
APP_START(usbtest)
.init = usbtest_init,
.entry = usbtest_entry,
APP_END
+61
Ver Arquivo
@@ -0,0 +1,61 @@
# Copyright 2016 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
assert(target_cpu == "arm")
import("//gnbuild/config.gni")
import("//gnbuild/generate_ld_script.gni")
module("arm") {
configs += [
"//kernel/kernel:enable_vm",
"//kernel/lib/console:enable_console",
"//kernel/lib/magenta:enable_magenta",
]
include_dirs = [
"//kernel/dev/interrupt/include",
"//kernel/lib/heap/include",
"//kernel/lib/io/include",
"//kernel/lib/libc/include",
"//kernel/lib/magenta/include",
"//kernel/lib/user_copy/include",
"//system/ulib/magenta/include",
]
# These defines would be ideally defined as a config, but they're currently
# also shared by the ld script target and as such has to be defined as
# variable.
defines = kernel_defines
sources = [
"arm/arch.c",
"arm/asm.S",
"arm/cache-ops.S",
"arm/cache.c",
"arm/debug.c",
"arm/exceptions.S",
"arm/faults.c",
"arm/fpu.c",
"arm/mmu.c",
"arm/ops.S",
"arm/start.S",
"arm/thread.c",
"arm/uspace_entry.S",
]
deps = [
":onesegment_ld",
]
if (enable_smp) {
include_dirs += [ "//kernel/dev/interrupt/include" ]
sources += [ "arm/mp.c" ]
deps += [ "//kernel/dev/interrupt/arm_gic" ]
}
}
generate_ld_script("onesegment_ld") {
input = "system-onesegment.ld"
output = "${root_gen_dir}/system-onesegment.ld"
defines = kernel_defines
}
+152
Ver Arquivo
@@ -0,0 +1,152 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <arch.h>
#include <arch/ops.h>
#include <arch/arm.h>
#include <kernel/thread.h>
#include <kernel/debug.h>
#include <platform.h>
#include <arch/arm/cm.h>
#include <target.h>
extern void *vectab;
#if ARM_CM_DYNAMIC_PRIORITY_SIZE
unsigned int arm_cm_num_irq_pri_bits;
unsigned int arm_cm_irq_pri_mask;
#endif
void arch_early_init(void)
{
arch_disable_ints();
#if (__CORTEX_M >= 0x03) || (CORTEX_SC >= 300)
uint i;
/* set the vector table base */
SCB->VTOR = (uint32_t)&vectab;
#if ARM_CM_DYNAMIC_PRIORITY_SIZE
/* number of priorities */
for (i=0; i < 7; i++) {
__set_BASEPRI(1 << i);
if (__get_BASEPRI() != 0)
break;
}
arm_cm_num_irq_pri_bits = 8 - i;
arm_cm_irq_pri_mask = ~((1 << i) - 1) & 0xff;
#endif
/* clear any pending interrupts and set all the vectors to medium priority */
uint groups = (SCnSCB->ICTR & 0xf) + 1;
for (i = 0; i < groups; i++) {
NVIC->ICER[i] = 0xffffffff;
NVIC->ICPR[i] = 0xffffffff;
for (uint j = 0; j < 32; j++) {
NVIC_SetPriority(i*32 + j, arm_cm_medium_priority());
}
}
/* leave BASEPRI at 0 */
__set_BASEPRI(0);
/* set priority grouping to 0 */
NVIC_SetPriorityGrouping(0);
/* enable certain faults */
SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | SCB_SHCSR_MEMFAULTENA_Msk);
/* set the svc and pendsv priority level to pretty low */
#endif
NVIC_SetPriority(SVCall_IRQn, arm_cm_lowest_priority());
NVIC_SetPriority(PendSV_IRQn, arm_cm_lowest_priority());
/* set systick and debugmonitor to medium priority */
NVIC_SetPriority(SysTick_IRQn, arm_cm_medium_priority());
#if (__CORTEX_M >= 0x03)
NVIC_SetPriority(DebugMonitor_IRQn, arm_cm_medium_priority());
#endif
#if ARM_WITH_CACHE
arch_enable_cache(UCACHE);
#endif
}
void arch_init(void)
{
#if ENABLE_CYCLE_COUNTER
*REG32(SCB_DEMCR) |= 0x01000000; // global trace enable
*REG32(DWT_CYCCNT) = 0;
*REG32(DWT_CTRL) |= 1; // enable cycle counter
#endif
}
void arch_quiesce(void)
{
}
void arch_idle(void)
{
__asm__ volatile("wfi");
}
#if (__CORTEX_M >= 0x03) || (CORTEX_SC >= 300)
void _arm_cm_set_irqpri(uint32_t pri)
{
if (pri == 0) {
__disable_irq(); // cpsid i
__set_BASEPRI(0);
} else if (pri >= 256) {
__set_BASEPRI(0);
__enable_irq();
} else {
uint32_t _pri = pri & arm_cm_irq_pri_mask;
if (_pri == 0)
__set_BASEPRI(1 << (8 - arm_cm_num_irq_pri_bits));
else
__set_BASEPRI(_pri);
__enable_irq(); // cpsie i
}
}
#endif
void arm_cm_irq_entry(void)
{
// Set PRIMASK to 1
// This is so that later calls to arch_ints_disabled() returns true while we're inside the int handler
// Note: this will probably screw up future efforts to stack higher priority interrupts since we're setting
// the cpu to essentially max interrupt priority here. Will have to rethink it then.
__disable_irq();
THREAD_STATS_INC(interrupts);
KEVLOG_IRQ_ENTER(__get_IPSR());
target_set_debug_led(1, true);
}
void arm_cm_irq_exit(bool reschedule)
{
target_set_debug_led(1, false);
if (reschedule)
arm_cm_trigger_preempt();
KEVLOG_IRQ_EXIT(__get_IPSR());
__enable_irq(); // clear PRIMASK
}
void arch_chain_load(void *entry, ulong arg0, ulong arg1, ulong arg2, ulong arg3)
{
PANIC_UNIMPLEMENTED;
}
+119
Ver Arquivo
@@ -0,0 +1,119 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <arch.h>
#include <arch/ops.h>
#include <arch/arm.h>
#include <kernel/thread.h>
#include <kernel/debug.h>
#include <platform.h>
#include <arch/arm/cm.h>
#if ARM_WITH_CACHE
/* cache flushing routines for cortex-m cores that support it */
void arch_disable_cache(uint flags)
{
if (flags & DCACHE)
SCB_DisableDCache();
if (flags & ICACHE)
SCB_DisableICache();
}
void arch_enable_cache(uint flags)
{
if (flags & DCACHE)
SCB_EnableDCache();
if (flags & ICACHE)
SCB_EnableICache();
}
/* clean (writeback) data in the data cache on the range */
void arch_clean_cache_range(addr_t start, size_t len)
{
addr_t end = start + len;
/* align the start address on CACHE_LINE boundary */
start &= ~(CACHE_LINE - 1);
SCB_CleanDCache_by_Addr((uint32_t *)start, end - start);
}
/* clean (writeback) and then evict data from the data cache on the range */
void arch_clean_invalidate_cache_range(addr_t start, size_t len)
{
addr_t end = start + len;
/* align the start address on CACHE_LINE boundary */
start &= ~(CACHE_LINE - 1);
SCB_CleanInvalidateDCache_by_Addr((uint32_t *)start, end - start);
}
/* evict data from the data cache on the range */
void arch_invalidate_cache_range(addr_t start, size_t len)
{
addr_t end = start + len;
/* align the start address on CACHE_LINE boundary */
start &= ~(CACHE_LINE - 1);
SCB_InvalidateDCache_by_Addr((uint32_t *)start, end - start);
}
/*
* clean (writeback) data on the range and then throw away the instruction cache,
* ensuring that new instructions fetched from the range are not stale.
*/
void arch_sync_cache_range(addr_t start, size_t len)
{
/* flush the dcache and invalidate the icache, ensuring fresh instructions */
arch_clean_cache_range(start, len);
SCB_InvalidateICache();
}
#else
/* doesn't support cache flush, just nop */
void arch_disable_cache(uint flags)
{
}
void arch_enable_cache(uint flags)
{
}
/* clean (writeback) data in the data cache on the range */
void arch_clean_cache_range(addr_t start, size_t len)
{
}
/* clean (writeback) and then evict data from the data cache on the range */
void arch_clean_invalidate_cache_range(addr_t start, size_t len)
{
}
/* evict data from the data cache on the range */
void arch_invalidate_cache_range(addr_t start, size_t len)
{
}
/*
* clean (writeback) data on the range and then throw away the instruction cache,
* ensuring that new instructions fetched from the range are not stale.
*/
void arch_sync_cache_range(addr_t start, size_t len)
{
}
#endif // !ARM_WITH_CACHE
+234
Ver Arquivo
@@ -0,0 +1,234 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012-2013 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <stdio.h>
#include <compiler.h>
#include <stdint.h>
#include <kernel/thread.h>
#include <arch/arm/cm.h>
#include <platform.h>
static void dump_frame(const struct arm_cm_exception_frame *frame)
{
printf("exception frame at %p\n", frame);
printf("\tr0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x r4 0x%08x\n",
frame->r0, frame->r1, frame->r2, frame->r3, frame->r4);
printf("\tr5 0x%08x r6 0x%08x r7 0x%08x r8 0x%08x r9 0x%08x\n",
frame->r5, frame->r6, frame->r7, frame->r8, frame->r9);
printf("\tr10 0x%08x r11 0x%08x r12 0x%08x\n",
frame->r10, frame->r11, frame->r12);
printf("\tlr 0x%08x pc 0x%08x psr 0x%08x\n",
frame->lr, frame->pc, frame->psr);
}
static void hardfault(struct arm_cm_exception_frame *frame)
{
printf("hardfault: ");
dump_frame(frame);
#if (__CORTEX_M >= 0X03) || (__CORTEX_SC >= 300)
printf("HFSR 0x%x\n", SCB->HFSR);
#endif
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
static void memmanage(struct arm_cm_exception_frame *frame)
{
printf("memmanage: ");
dump_frame(frame);
#if (__CORTEX_M >= 0X03) || (__CORTEX_SC >= 300)
uint32_t mmfsr = SCB->CFSR & 0xff;
if (mmfsr & (1<<0)) { // IACCVIOL
printf("instruction fault\n");
}
if (mmfsr & (1<<1)) { // DACCVIOL
printf("data fault\n");
}
if (mmfsr & (1<<3)) { // MUNSTKERR
printf("fault on exception return\n");
}
if (mmfsr & (1<<4)) { // MSTKERR
printf("fault on exception entry\n");
}
if (mmfsr & (1<<5)) { // MLSPERR
printf("fault on lazy fpu preserve\n");
}
if (mmfsr & (1<<7)) { // MMARVALID
printf("fault address 0x%x\n", SCB->MMFAR);
}
#endif
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
static void usagefault(struct arm_cm_exception_frame *frame)
{
printf("usagefault: ");
dump_frame(frame);
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
static void busfault(struct arm_cm_exception_frame *frame)
{
printf("busfault: ");
dump_frame(frame);
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
/* raw exception vectors */
void _nmi(void)
{
printf("nmi\n");
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
#if (__CORTEX_M >= 0X03) || (__CORTEX_SC >= 300)
__NAKED void _hardfault(void)
{
__asm__ volatile(
"push {r4-r11};"
"mov r0, sp;"
"b %0;"
:: "i" (hardfault)
);
__UNREACHABLE;
}
void _memmanage(void)
{
__asm__ volatile(
"push {r4-r11};"
"mov r0, sp;"
"b %0;"
:: "i" (memmanage)
);
__UNREACHABLE;
}
void _busfault(void)
{
__asm__ volatile(
"push {r4-r11};"
"mov r0, sp;"
"b %0;"
:: "i" (busfault)
);
__UNREACHABLE;
}
void _usagefault(void)
{
__asm__ volatile(
"push {r4-r11};"
"mov r0, sp;"
"b %0;"
:: "i" (usagefault)
);
__UNREACHABLE;
}
#else
__NAKED void _hardfault(void)
{
struct arm_cm_exception_frame *frame;
__asm__ volatile(
"push {r4-r7};"
"mov r4, r8;"
"mov r5, r9;"
"mov r6, r10;"
"mov r7, r11;"
"push {r4-r7};"
"mov %0, sp;"
: "=r" (frame):
);
printf("hardfault: ");
dump_frame(frame);
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
__UNREACHABLE;
}
void _memmanage(void)
{
struct arm_cm_exception_frame *frame;
__asm__ volatile(
"push {r4-r7};"
"mov r4, r8;"
"mov r5, r9;"
"mov r6, r10;"
"mov r7, r11;"
"push {r4-r7};"
"mov %0, sp;"
: "=r" (frame):
);
printf("memmanage: ");
dump_frame(frame);
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
__UNREACHABLE;
}
void _busfault(void)
{
struct arm_cm_exception_frame *frame;
__asm__ volatile(
"push {r4-r7};"
"mov r4, r8;"
"mov r5, r9;"
"mov r6, r10;"
"mov r7, r11;"
"push {r4-r7};"
"mov %0, sp;"
: "=r" (frame):
);
printf("busfault: ");
dump_frame(frame);
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
__UNREACHABLE;
}
void _usagefault(void)
{
struct arm_cm_exception_frame *frame;
__asm__ volatile(
"push {r4-r7};"
"mov r4, r8;"
"mov r5, r9;"
"mov r6, r10;"
"mov r7, r11;"
"push {r4-r7};"
"mov %0, sp;"
: "=r" (frame):
);
printf("usagefault: ");
dump_frame(frame);
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
__UNREACHABLE;
}
#endif
/* systick handler */
void __WEAK _systick(void)
{
printf("systick\n");
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
void __WEAK _debugmonitor(void)
{
printf("debugmonitor\n");
platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC);
}
@@ -0,0 +1,20 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef __ARM_M_ARCH_THREAD_H
#define __ARM_M_ARCH_THREAD_H
#include <stdbool.h>
#include <sys/types.h>
struct arch_thread {
vaddr_t sp;
bool was_preempted;
};
#endif
+173
Ver Arquivo
@@ -0,0 +1,173 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012-2013 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
/* support header for all cortex-m class cpus */
#include <compiler.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <platform/platform_cm.h>
__BEGIN_CDECLS
#if ARM_CPU_CORTEX_M0
#include <core_cm0.h>
#elif ARM_CPU_CORTEX_M0_PLUS
#include <core_cm0plus.h>
#elif ARM_CPU_CORTEX_M3
#include <core_cm3.h>
#elif ARM_CPU_CORTEX_M4
#include <core_cm4.h>
#elif ARM_CPU_CORTEX_M7
#include <core_cm7.h>
#else
#error "unknown cortex-m core"
#endif
/* registers dealing with the cycle counter */
#define DWT_CTRL (0xE0001000)
#define DWT_CYCCNT (0xE0001004)
#define SCB_DEMCR (0xE000EDFC)
struct arm_cm_exception_frame {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t psr;
};
struct arm_cm_exception_frame_short {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t lr;
uint32_t pc;
uint32_t psr;
};
struct arm_cm_exception_frame_long {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t lr;
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r12;
uint32_t exc_lr;
uint32_t pc;
uint32_t psr;
};
#if ARM_CM_DYNAMIC_PRIORITY_SIZE
extern unsigned int arm_cm_num_irq_pri_bits;
extern unsigned int arm_cm_irq_pri_mask;
#else
/* if we don't want to calculate the nubmer of priority bits, then assume
* the cpu implements 3 (8 priority levels), which is the minimum according to spec.
*/
#ifndef __NVIC_PRIO_BITS
#define __NVIC_PRIO_BITS 3
#endif
static const unsigned int arm_cm_num_irq_pri_bits = __NVIC_PRIO_BITS;
static const unsigned int arm_cm_irq_pri_mask = ~((1 << __NVIC_PRIO_BITS) - 1) & 0xff;
#endif
#if (__CORTEX_M >= 0x03) || (CORTEX_SC >= 300)
void _arm_cm_set_irqpri(uint32_t pri);
static void arm_cm_set_irqpri(uint32_t pri)
{
if (__ISCONSTANT(pri)) {
if (pri == 0) {
__disable_irq(); // cpsid i
__set_BASEPRI(0);
} else if (pri >= 256) {
__set_BASEPRI(0);
__enable_irq();
} else {
uint32_t _pri = pri & arm_cm_irq_pri_mask;
if (_pri == 0)
__set_BASEPRI(1 << (8 - arm_cm_num_irq_pri_bits));
else
__set_BASEPRI(_pri);
__enable_irq(); // cpsie i
}
} else {
_arm_cm_set_irqpri(pri);
}
}
#endif
static inline uint32_t arm_cm_highest_priority(void)
{
return 0;
}
static inline uint32_t arm_cm_lowest_priority(void)
{
return (1 << arm_cm_num_irq_pri_bits) - 1;
}
static inline uint32_t arm_cm_medium_priority(void)
{
return (1 << (arm_cm_num_irq_pri_bits - 1));
}
#if (__CORTEX_M >= 0x03) || (CORTEX_SC >= 300)
static inline void arm_cm_trigger_interrupt(int vector)
{
NVIC->STIR = vector;
}
#endif
static inline void arm_cm_trigger_preempt(void)
{
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
}
/* systick */
void arm_cm_systick_init(uint32_t mhz);
/* extern void _systick(void); // override this */
/* interrupt glue */
/*
* Platform code should put this as the first and last line of their irq handlers.
* Pass true to reschedule to request a preempt.
*/
void arm_cm_irq_entry(void);
void arm_cm_irq_exit(bool reschedule);
__END_CDECLS
+88
Ver Arquivo
@@ -0,0 +1,88 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2013 Google Inc.
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <compiler.h>
#include <kernel/debug.h>
__ALIGNED(8) __NAKED
#if (__CORTEX_M >= 0x03) || (CORTEX_SC >= 300)
void spin_cycles(uint32_t cycles)
{
asm (
/* 4 cycles per loop, subtract out 8 cycles for the overhead of the next
* 4 instructions, plus the call into and return from the function.
* Then, add 3 then >> 2 to round up to the number of loop iterations.
*/
"subs r1, %[cycles], #5\n"
"asrs r1, r1, #2\n"
"ble .Ldone\n"
/* Padding to stay aligned on an 8 byte boundary, also has the added
* advantage of normalizing the overhead (1+1+2 cycles if the branch is
* take, or 1+1+1+1 cycles if the branch is skipped and the nop is
* executed)
*/
"nop\n"
/* Main delay loop.
* sub is 1 cycle
* nop is 1 cycle
* branch is 2 cycles
*/
".Lloop:\n"
"subs r1, r1, #1\n"
"nop\n"
"bne .Lloop\n"
".Ldone:\n"
"bx lr\n"
: /* no output */
: [cycles] "r" (cycles) /* input is cycles */
: "r1" /* r1 gets clobbered */
);
}
#else
/* Cortex-M0 & Cortex-M0+ */
void spin_cycles(uint32_t cycles)
{
asm (
/* 4 cycles per loop, subtract out 8 cycles for the overhead of the next
* 4 instructions, plus the call into and return from the function.
* Then, add 3 then >> 2 to round up to the number of loop iterations.
*/
"sub r1, %[cycles], #5\n"
"asr r1, r1, #2\n"
"cmp r1, #0\n"
"ble .Ldone\n"
/* Padding to stay aligned on an 8 byte boundary, also has the added
* advantage of normalizing the overhead (1+1+2 cycles if the branch is
* take, or 1+1+1+1 cycles if the branch is skipped and the nop is
* executed)
*/
"nop\n"
/* Main delay loop.
* sub is 1 cycle
* nop is 1 cycle
* branch is 2 cycles
*/
".Lloop:\n"
"sub r1, r1, #1\n"
"cmp r1,#0\n"
"bne .Lloop\n"
".Ldone:\n"
"bx lr\n"
: /* no output */
: [cycles] "r" (cycles) /* input is cycles */
: "r1" /* r1 gets clobbered */
);
}
#endif
+35
Ver Arquivo
@@ -0,0 +1,35 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <compiler.h>
#include <stdint.h>
/* externals */
extern unsigned int __data_start_rom, __data_start, __data_end;
extern unsigned int __bss_start, __bss_end;
extern void lk_main(void) __NO_RETURN __EXTERNALLY_VISIBLE;
void _start(void)
{
/* copy data from rom */
if (&__data_start != &__data_start_rom) {
unsigned int *src = &__data_start_rom;
unsigned int *dest = &__data_start;
while (dest != &__data_end)
*dest++ = *src++;
}
/* zero out bss */
unsigned int *bss = &__bss_start;
while (bss != &__bss_end)
*bss++ = 0;
lk_main();
}
+15
Ver Arquivo
@@ -0,0 +1,15 @@
# Copyright 2016 The Fuchsia Authors
# Copyright (c) 2008-2015 Travis Geiselbrecht
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/systick.c
include make/module.mk
+130
Ver Arquivo
@@ -0,0 +1,130 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012-2014 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
/*
* Generic systick timer support for providing system time (current_time(), current_time_hires()),
* and a monotonic timer for the kernel.
*/
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <debug.h>
#include <assert.h>
#include <trace.h>
#include <err.h>
#include <kernel/thread.h>
#include <arch/arm.h>
#include <arch/arm/cm.h>
#include <platform.h>
#include <platform/timer.h>
#define LOCAL_TRACE 0
static volatile uint64_t ticks;
static uint32_t tick_rate = 0;
static uint32_t tick_rate_mhz = 0;
static lk_time_t tick_interval_ms;
static lk_bigtime_t tick_interval_us;
static platform_timer_callback cb;
static void *cb_args;
static void arm_cm_systick_set_periodic(lk_time_t period)
{
LTRACEF("clk_freq %u, period %u\n", tick_rate, (uint)period);
uint32_t ticks = tick_rate / (1000 / period);
LTRACEF("ticks %d\n", ticks);
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}
static void arm_cm_systick_cancel_periodic(void)
{
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
/* main systick irq handler */
void _systick(void)
{
ticks++;
arm_cm_irq_entry();
bool resched = false;
if (cb) {
lk_time_t now = current_time();
if (cb(cb_args, now) == INT_RESCHEDULE)
resched = true;
}
arm_cm_irq_exit(resched);
}
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
{
LTRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
DEBUG_ASSERT(tick_rate != 0 && tick_rate_mhz != 0);
cb = callback;
cb_args = arg;
tick_interval_ms = interval;
tick_interval_us = interval * 1000;
arm_cm_systick_set_periodic(interval);
return NO_ERROR;
}
lk_time_t current_time(void)
{
uint32_t reload = SysTick->LOAD & SysTick_LOAD_RELOAD_Msk;
uint64_t t;
uint32_t delta;
do {
t = ticks;
delta = (volatile uint32_t)SysTick->VAL;
DMB;
} while (ticks != t);
/* convert ticks to msec */
delta = (reload - delta) / (tick_rate_mhz * 1000);
lk_time_t res = (t * tick_interval_ms) + delta;
return res;
}
lk_bigtime_t current_time_hires(void)
{
uint32_t reload = SysTick->LOAD & SysTick_LOAD_RELOAD_Msk;
uint64_t t;
uint32_t delta;
do {
t = ticks;
delta = (volatile uint32_t)SysTick->VAL;
DMB;
} while (ticks != t);
/* convert ticks to usec */
delta = (reload - delta) / tick_rate_mhz;
lk_bigtime_t res = (t * tick_interval_us) + delta;
return res;
}
void arm_cm_systick_init(uint32_t mhz)
{
tick_rate = mhz;
tick_rate_mhz = mhz / 1000000;
}
+314
Ver Arquivo
@@ -0,0 +1,314 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <debug.h>
#include <trace.h>
#include <assert.h>
#include <kernel/thread.h>
#include <arch/arm.h>
#include <arch/arm/cm.h>
#define LOCAL_TRACE 0
struct arm_cm_context_switch_frame {
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t lr;
};
/* since we're implicitly uniprocessor, store a pointer to the current thread here */
thread_t *_current_thread;
void arch_thread_initialize(struct thread *t, vaddr_t entry_point)
{
LTRACEF("thread %p, stack %p\n", t, t->stack);
/* find the top of the stack and align it on an 8 byte boundary */
uint32_t *sp = (void *)ROUNDDOWN((vaddr_t)t->stack + t->stack_size, 8);
struct arm_cm_context_switch_frame *frame = (void *)sp;
frame--;
/* arrange for lr to point to our starting routine */
frame->lr = (uint32_t)entry_point;
t->arch.sp = (addr_t)frame;
t->arch.was_preempted = false;
}
volatile struct arm_cm_exception_frame_long *preempt_frame;
static void pendsv(struct arm_cm_exception_frame_long *frame)
{
arch_disable_ints();
LTRACEF("preempting thread %p (%s)\n", _current_thread, _current_thread->name);
/* save the iframe the pendsv fired on and hit the preemption code */
preempt_frame = frame;
thread_preempt();
LTRACEF("fell through\n");
/* if we got here, there wasn't anything to switch to, so just fall through and exit */
preempt_frame = NULL;
arch_enable_ints();
}
/*
* raw pendsv exception handler, triggered by interrupt glue to schedule
* a preemption check.
*/
__NAKED void _pendsv(void)
{
__asm__ volatile(
#if (__CORTEX_M >= 0x03)
"push { r4-r11, lr };"
"mov r0, sp;"
"bl %0;"
"pop { r4-r11, lr };"
"bx lr;"
#else
"push { lr };"
"mov r0, r8;"
"mov r1, r9;"
"mov r2, r10;"
"mov r3, r11;"
"push { r0-r3 };"
"push { r4-r7 };"
"mov r0, sp;"
"bl %c0;"
"pop { r4-r7 };"
"pop { r0-r3 };"
"mov r8 , r0;"
"mov r9 , r1;"
"mov r10, r2;"
"mov r11, r3;"
"pop { r0 };"
"mov lr, r0;"
"bx lr;"
#endif
:: "i" (pendsv)
);
__UNREACHABLE;
}
/*
* svc handler, used to hard switch the cpu into exception mode to return
* to preempted thread.
*/
__NAKED void _svc(void)
{
__asm__ volatile(
/* load the pointer to the original exception frame we want to restore */
#if (__CORTEX_M >= 0x03)
"mov sp, r4;"
"pop { r4-r11, lr };"
"bx lr;"
#else
"mov sp, r4;"
"pop { r4-r7 };"
"pop { r0-r3 };"
"mov r8 , r0;"
"mov r9 , r1;"
"mov r10, r2;"
"mov r11, r3;"
"pop { pc };"
#endif
);
}
__NAKED static void _half_save_and_svc(vaddr_t *fromsp, vaddr_t tosp)
{
__asm__ volatile(
#if (__CORTEX_M >= 0x03)
"push { r4-r11, lr };"
"str sp, [r0];"
/* make sure we load the destination sp here before we reenable interrupts */
"mov sp, r1;"
"clrex;"
"cpsie i;"
"mov r4, r1;"
"svc #0;" /* make a svc call to get us into handler mode */
#else
"push { lr };"
"mov r2, r10;"
"mov r3, r11;"
"push { r2-r3 };"
"mov r2, r8;"
"mov r3, r9;"
"push { r2-r3 };"
"push { r4-r7 };"
"mov r3, sp;"
"str r3, [r0];"
"mov sp, r1;"
"cpsie i;"
"mov r4, r1;"
"svc #0;" /* make a svc call to get us into handler mode */
#endif
);
}
/* simple scenario where the to and from thread yielded */
__NAKED static void _arch_non_preempt_context_switch(vaddr_t *fromsp, vaddr_t tosp)
{
__asm__ volatile(
#if (__CORTEX_M >= 0x03)
"push { r4-r11, lr };"
"str sp, [r0];"
"mov sp, r1;"
"pop { r4-r11, lr };"
"clrex;"
"bx lr;"
#else
"push { lr };"
"mov r2, r10;"
"mov r3, r11;"
"push { r2-r3 };"
"mov r2, r8;"
"mov r3, r9;"
"push { r2-r3 };"
"push { r4-r7 };"
"mov r3, sp;"
"str r3, [r0];"
"mov sp, r1;"
"pop { r4-r7 };"
"pop { r0-r3 };"
"mov r8 , r0;"
"mov r9 , r1;"
"mov r10, r2;"
"mov r11, r3;"
"pop { pc };"
#endif
);
}
__NAKED static void _thread_mode_bounce(void)
{
__asm__ volatile(
#if (__CORTEX_M >= 0x03)
"pop { r4-r11, lr };"
"bx lr;"
#else
"pop { r4-r7 };"
"pop { r0-r3 };"
"mov r8 , r0;"
"mov r9 , r1;"
"mov r10, r2;"
"mov r11, r3;"
"pop { pc };"
#endif
);
__UNREACHABLE;
}
/*
* The raw context switch routine. Called by the scheduler when it decides to switch.
* Called either in the context of a thread yielding or blocking (interrupts disabled,
* on the system stack), or inside the pendsv handler on a thread that is being preempted
* (interrupts disabled, in handler mode). If preempt_frame is set the thread
* is being preempted.
*/
void arch_context_switch(struct thread *oldthread, struct thread *newthread)
{
LTRACE_ENTRY;
/* if preempt_frame is set, we are being preempted */
if (preempt_frame) {
oldthread->arch.was_preempted = true;
oldthread->arch.sp = (addr_t)preempt_frame;
preempt_frame = NULL;
LTRACEF("we're preempted, new %d\n", newthread->arch.was_preempted);
if (newthread->arch.was_preempted) {
/* return directly to the preempted thread's iframe */
__asm__ volatile(
"mov sp, %0;"
#if (__CORTEX_M >= 0x03)
"cpsie i;"
"pop { r4-r11, lr };"
"clrex;"
"bx lr;"
#else
"cpsie i;"
"pop { r4-r7 };"
"pop { r0-r3 };"
"mov r8 , r0;"
"mov r9 , r1;"
"mov r10, r2;"
"mov r11, r3;"
"pop { pc };"
#endif
:: "r"(newthread->arch.sp)
);
__UNREACHABLE;
} else {
/* we're inside a pendsv, switching to a user mode thread */
/* set up a fake frame to exception return to */
struct arm_cm_exception_frame_short *frame = (void *)newthread->arch.sp;
frame--;
frame->pc = (uint32_t)&_thread_mode_bounce;
frame->psr = (1 << 24); /* thread bit set, IPSR 0 */
frame->r0 = frame->r1 = frame->r2 = frame->r3 = frame->r12 = frame->lr = 99;
LTRACEF("iretting to user space\n");
//hexdump(frame, sizeof(*frame) + 64);
__asm__ volatile(
#if (__CORTEX_M >= 0x03)
"clrex;"
#endif
"mov sp, %0;"
"bx %1;"
:: "r"(frame), "r"(0xfffffff9)
);
__UNREACHABLE;
}
} else {
oldthread->arch.was_preempted = false;
if (newthread->arch.was_preempted) {
LTRACEF("not being preempted, but switching to preempted thread\n");
_half_save_and_svc(&oldthread->arch.sp, newthread->arch.sp);
} else {
/* fast path, both sides did not preempt */
_arch_non_preempt_context_switch(&oldthread->arch.sp, newthread->arch.sp);
}
}
}
void arch_dump_thread(thread_t *t)
{
if (t->state != THREAD_RUNNING) {
dprintf(INFO, "\tarch: ");
dprintf(INFO, "sp 0x%lx, was preempted %u\n", t->arch.sp, t->arch.was_preempted);
}
}
+59
Ver Arquivo
@@ -0,0 +1,59 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <compiler.h>
#include <stdint.h>
/*
* Make a nice 8 byte aligned stack to run on before the threading system is up.
* Put it in the .bss.prebss.* section to make sure it doesn't get wiped
* when bss is cleared a little ways into boot.
*/
static uint8_t initial_stack[1024] __SECTION(".bss.prebss.initial_stack") __ALIGNED(8);
extern void _start(void);
extern void _nmi(void);
extern void _hardfault(void);
extern void _memmanage(void);
extern void _busfault(void);
extern void _usagefault(void);
extern void _svc(void);
extern void _debugmonitor(void);
extern void _pendsv(void);
extern void _systick(void);
#if defined(WITH_DEBUGGER_INFO)
extern struct __debugger_info__ _debugger_info;
#endif
const void *const __SECTION(".text.boot.vectab1") vectab[] = {
/* arm exceptions */
initial_stack + sizeof(initial_stack),
_start,
_nmi, // nmi
_hardfault, // hard fault
_memmanage, // mem manage
_busfault, // bus fault
_usagefault, // usage fault
0, // reserved
#if defined(WITH_DEBUGGER_INFO)
(void *) 0x52474244,
&_debugger_info,
#else
0, // reserved
0, // reserved
#endif
0, // reserved
_svc, // svcall
_debugmonitor, // debug monitor
0, // reserved
_pendsv, // pendsv
_systick, // systick
};
+430
Ver Arquivo
@@ -0,0 +1,430 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <debug.h>
#include <trace.h>
#include <stdlib.h>
#include <err.h>
#include <trace.h>
#include <stdio.h>
#include <reg.h>
#include <arch.h>
#include <arch/ops.h>
#include <arch/mmu.h>
#include <arch/arm.h>
#include <arch/arm/mmu.h>
#include <arch/mp.h>
#include <kernel/spinlock.h>
#include <kernel/thread.h>
#include <lk/main.h>
#include <lk/init.h>
#include <platform.h>
#include <target.h>
#include <kernel/thread.h>
#include <kernel/vm.h>
#define LOCAL_TRACE 0
#if WITH_DEV_TIMER_ARM_CORTEX_A9
#include <dev/timer/arm_cortex_a9.h>
#endif
#if WITH_DEV_INTERRUPT_ARM_GIC
#include <dev/interrupt/arm_gic.h>
#endif
#if WITH_DEV_CACHE_PL310
#include <dev/cache/pl310.h>
#endif
/* initial and abort stacks */
uint8_t abort_stack[ARCH_DEFAULT_STACK_SIZE *SMP_MAX_CPUS] __CPU_ALIGN;
static void arm_basic_setup(void);
static void spinlock_test(void);
static void spinlock_test_secondary(void);
#if WITH_SMP
/* smp boot lock */
spin_lock_t arm_boot_cpu_lock = 1;
volatile int secondaries_to_init = 0;
uint arm_num_cpus = 1;
#endif
void arch_early_init(void)
{
/* turn off the cache */
arch_disable_cache(UCACHE);
#if WITH_DEV_CACHE_PL310
pl310_set_enable(false);
#endif
arm_basic_setup();
#if WITH_SMP && ARM_CPU_CORTEX_A9
/* enable snoop control */
addr_t scu_base = arm_read_cbar();
*REG32(scu_base) |= (1<<0); /* enable SCU */
#endif
#if ARM_WITH_MMU
arm_mmu_early_init();
platform_init_mmu_mappings();
#endif
/* turn the cache back on */
#if WITH_DEV_CACHE_PL310
pl310_set_enable(true);
#endif
arch_enable_cache(UCACHE);
}
void arch_init(void)
{
#if WITH_SMP
arch_mp_init_percpu();
LTRACEF("midr 0x%x\n", arm_read_midr());
LTRACEF("sctlr 0x%x\n", arm_read_sctlr());
LTRACEF("actlr 0x%x\n", arm_read_actlr());
#if ARM_CPU_CORTEX_A9
LTRACEF("cbar 0x%x\n", arm_read_cbar());
#endif
LTRACEF("mpidr 0x%x\n", arm_read_mpidr());
LTRACEF("ttbcr 0x%x\n", arm_read_ttbcr());
LTRACEF("ttbr0 0x%x\n", arm_read_ttbr0());
LTRACEF("dacr 0x%x\n", arm_read_dacr());
#if ARM_CPU_CORTEX_A7
LTRACEF("l2ctlr 0x%x\n", arm_read_l2ctlr());
LTRACEF("l2ectlr 0x%x\n", arm_read_l2ectlr());
#endif
#if ARM_CPU_CORTEX_A9
addr_t scu_base = arm_read_cbar();
uint32_t scu_config = *REG32(scu_base + 4);
secondaries_to_init = scu_config & 0x3;
#elif ARM_CPU_CORTEX_A7 || ARM_CPU_CORTEX_A15
uint32_t l2ctlr = arm_read_l2ctlr();
secondaries_to_init = (l2ctlr >> 24);
#else
secondaries_to_init = SMP_MAX_CPUS - 1; /* TODO: get count from somewhere else, or add cpus as they boot */
#endif
arm_num_cpus += secondaries_to_init;
lk_init_secondary_cpus(secondaries_to_init);
/* in platforms where the cpus have already been started, go ahead and wake up all the
* secondary cpus here.
*/
dprintf(SPEW, "releasing %d secondary cpu%c\n", secondaries_to_init, secondaries_to_init != 1 ? 's' : ' ');
/* release the secondary cpus */
spin_unlock(&arm_boot_cpu_lock);
/* flush the release of the lock, since the secondary cpus are running without cache on */
arch_clean_cache_range((addr_t)&arm_boot_cpu_lock, sizeof(arm_boot_cpu_lock));
#if ARM_ARCH_WAIT_FOR_SECONDARIES
/* wait for secondary cpus to boot before arm_mmu_init below, which will remove
* temporary boot mappings
* TODO: find a cleaner way to do this than this #define
*/
while (secondaries_to_init > 0) {
arch_spinloop_pause();
}
#endif
#endif // WITH_SMP
//spinlock_test();
#if ARM_WITH_MMU
/* finish intializing the mmu */
arm_mmu_init();
#endif
}
#if WITH_SMP
void arm_secondary_entry(uint asm_cpu_num)
{
uint cpu = arch_curr_cpu_num();
if (cpu != asm_cpu_num)
return;
arm_basic_setup();
/* enable the local L1 cache */
//arch_enable_cache(UCACHE);
// XXX may not be safe, but just hard enable i and d cache here
// at the moment cannot rely on arch_enable_cache not dumping the L2
uint32_t sctlr = arm_read_sctlr();
sctlr |= (1<<12) | (1<<2); // enable i and dcache
arm_write_sctlr(sctlr);
/* run early secondary cpu init routines up to the threading level */
lk_init_level(LK_INIT_FLAG_SECONDARY_CPUS, LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_THREADING - 1);
arch_mp_init_percpu();
LTRACEF("cpu num %d\n", cpu);
LTRACEF("sctlr 0x%x\n", arm_read_sctlr());
LTRACEF("actlr 0x%x\n", arm_read_actlr());
/* we're done, tell the main cpu we're up */
atomic_add(&secondaries_to_init, -1);
smp_mb();
arch_spinloop_signal();
lk_secondary_cpu_entry();
}
#endif
static void arm_basic_setup(void)
{
uint32_t sctlr = arm_read_sctlr();
/* ARMV7 bits */
sctlr &= ~(1<<10); /* swp disable */
sctlr |= (1<<11); /* enable program flow prediction */
sctlr &= ~(1<<14); /* random cache/tlb replacement */
sctlr &= ~(1<<25); /* E bit set to 0 on exception */
sctlr &= ~(1<<30); /* no thumb exceptions */
arm_write_sctlr(sctlr);
uint32_t actlr = arm_read_actlr();
#if ARM_CPU_CORTEX_A9
actlr |= (1<<2); /* enable dcache prefetch */
#if WITH_DEV_CACHE_PL310
actlr |= (1<<7); /* L2 exclusive cache */
actlr |= (1<<3); /* L2 write full line of zeroes */
actlr |= (1<<1); /* L2 prefetch hint enable */
#endif
#if WITH_SMP
/* enable smp mode, cache and tlb broadcast */
actlr |= (1<<6) | (1<<0);
#endif
#endif // ARM_CPU_CORTEX_A9
#if ARM_CPU_CORTEX_A7
#if WITH_SMP
/* enable smp mode */
actlr |= (1<<6);
#endif
#endif // ARM_CPU_CORTEX_A7
arm_write_actlr(actlr);
#if ENABLE_CYCLE_COUNTER && ARM_ISA_ARMV7
/* enable the cycle count register */
uint32_t en;
__asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (en));
en &= ~(1<<3); /* cycle count every cycle */
en |= 1; /* enable all performance counters */
__asm__ volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" (en));
/* enable cycle counter */
en = (1<<31);
__asm__ volatile("mcr p15, 0, %0, c9, c12, 1" :: "r" (en));
#endif
#if ARM_WITH_VFP
/* enable cp10 and cp11 */
uint32_t val = arm_read_cpacr();
val |= (3<<22)|(3<<20);
arm_write_cpacr(val);
/* set enable bit in fpexc */
__asm__ volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (val));
val |= (1<<30);
__asm__ volatile("mcr p10, 7, %0, c8, c0, 0" :: "r" (val));
/* make sure the fpu starts off disabled */
arm_fpu_set_enable(false);
#endif
/* set the vector base to our exception vectors so we dont need to double map at 0 */
#if ARM_ISA_ARMV7
arm_write_vbar(KERNEL_BASE + KERNEL_LOAD_OFFSET);
#endif
}
void arch_quiesce(void)
{
#if ENABLE_CYCLE_COUNTER
#if ARM_ISA_ARMV7
/* disable the cycle count and performance counters */
uint32_t en;
__asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (en));
en &= ~1; /* disable all performance counters */
__asm__ volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" (en));
/* disable cycle counter */
en = 0;
__asm__ volatile("mcr p15, 0, %0, c9, c12, 1" :: "r" (en));
#endif
#if ARM_CPU_ARM1136
/* disable the cycle count and performance counters */
uint32_t en;
__asm__ volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (en));
en &= ~1; /* disable all performance counters */
__asm__ volatile("mcr p15, 0, %0, c15, c12, 0" :: "r" (en));
#endif
#endif
uint32_t actlr = arm_read_actlr();
#if ARM_CPU_CORTEX_A9
actlr = 0; /* put the aux control register back to default */
#endif // ARM_CPU_CORTEX_A9
arm_write_actlr(actlr);
}
#if ARM_ISA_ARMV7
/* virtual to physical translation */
status_t arm_vtop(addr_t va, addr_t *pa)
{
spin_lock_saved_state_t irqstate;
arch_interrupt_save(&irqstate, SPIN_LOCK_FLAG_INTERRUPTS);
arm_write_ats1cpr(va & ~(PAGE_SIZE-1));
uint32_t par = arm_read_par();
arch_interrupt_restore(irqstate, SPIN_LOCK_FLAG_INTERRUPTS);
if (par & 1)
return ERR_NOT_FOUND;
if (pa) {
*pa = (par & 0xfffff000) | (va & 0xfff);
}
return NO_ERROR;
}
#endif
void arch_chain_load(void *entry, ulong arg0, ulong arg1, ulong arg2, ulong arg3)
{
/* disabled due to changing VMM interfaces.
* resurrect when the vmm is stabilized and the need for a chain load arises.
*/
#if 0
LTRACEF("entry %p, args 0x%lx 0x%lx 0x%lx 0x%lx\n", entry, arg0, arg1, arg2, arg3);
/* we are going to shut down the system, start by disabling interrupts */
arch_disable_ints();
/* give target and platform a chance to put hardware into a suitable
* state for chain loading.
*/
target_quiesce();
platform_quiesce();
paddr_t entry_pa;
paddr_t loader_pa;
#if WITH_KERNEL_VM
/* get the physical address of the entry point we're going to branch to */
if (arm_vtop((addr_t)entry, &entry_pa) < 0) {
panic("error translating entry physical address\n");
}
/* add the low bits of the virtual address back */
entry_pa |= ((addr_t)entry & 0xfff);
LTRACEF("entry pa 0x%lx\n", entry_pa);
/* figure out the mapping for the chain load routine */
if (arm_vtop((addr_t)&arm_chain_load, &loader_pa) < 0) {
panic("error translating loader physical address\n");
}
/* add the low bits of the virtual address back */
loader_pa |= ((addr_t)&arm_chain_load & 0xfff);
paddr_t loader_pa_section = ROUNDDOWN(loader_pa, SECTION_SIZE);
LTRACEF("loader address %p, phys 0x%lx, surrounding large page 0x%lx\n",
&arm_chain_load, loader_pa, loader_pa_section);
/* using large pages, map around the target location */
arch_mmu_map(&vmm_get_kernel_aspace()->arch_aspace, loader_pa_section, loader_pa_section, (2 * SECTION_SIZE / PAGE_SIZE), 0);
#else
/* for non vm case, just branch directly into it */
entry_pa = (paddr_t)entry;
loader_pa = (paddr_t)&arm_chain_load;
#endif
LTRACEF("disabling instruction/data cache\n");
arch_disable_cache(UCACHE);
#if WITH_DEV_CACHE_PL310
pl310_set_enable(false);
#endif
/* put the booting cpu back into close to a default state */
arch_quiesce();
LTRACEF("branching to physical address of loader\n");
/* branch to the physical address version of the chain loader routine */
void (*loader)(paddr_t entry, ulong, ulong, ulong, ulong) __NO_RETURN = (void *)loader_pa;
loader(entry_pa, arg0, arg1, arg2, arg3);
#else
PANIC_UNIMPLEMENTED;
#endif
}
static spin_lock_t lock = 0;
static void spinlock_test(void)
{
TRACE_ENTRY;
spin_lock_saved_state_t state;
spin_lock_irqsave(&lock, state);
TRACEF("cpu0: i have the lock\n");
spin(1000000);
TRACEF("cpu0: releasing it\n");
spin_unlock_irqrestore(&lock, state);
spin(1000000);
}
static void spinlock_test_secondary(void)
{
TRACE_ENTRY;
spin(500000);
spin_lock_saved_state_t state;
spin_lock_irqsave(&lock, state);
TRACEF("cpu1: i have the lock\n");
spin(250000);
TRACEF("cpu1: releasing it\n");
spin_unlock_irqrestore(&lock, state);
}
/* switch to user mode, set the user stack pointer to user_stack_top, put the svc stack pointer to the top of the kernel stack */
void arch_enter_uspace(vaddr_t entry_point, vaddr_t user_stack_top, void *thread_arg)
{
DEBUG_ASSERT(IS_ALIGNED(user_stack_top, 8));
thread_t *ct = get_current_thread();
vaddr_t kernel_stack_top = (uintptr_t)ct->stack + ct->stack_size;
kernel_stack_top = ROUNDDOWN(kernel_stack_top, 8);
uint32_t spsr = CPSR_MODE_USR;
spsr |= (entry_point & 1) ? CPSR_THUMB : 0;
arch_disable_ints();
extern void arm_uspace_entry(void *thread_arg, vaddr_t kstack, vaddr_t *ustack, uint32_t spsr, vaddr_t entry_point) __NO_RETURN;
arm_uspace_entry(thread_arg, kernel_stack_top, &user_stack_top, spsr, entry_point);
__UNREACHABLE;
}
+128
Ver Arquivo
@@ -0,0 +1,128 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <asm.h>
#include <arch/arm/cores.h>
/* context switch frame is as follows:
* lr
* r11
* r10
* r9
* r8
* r7
* r6
* r5
* r4
* cp15_ro
* cp15_rw
*/
/* arm_context_switch(addr_t *old_sp, addr_t new_sp) */
FUNCTION(arm_context_switch)
/* save non callee trashed supervisor registers */
/* spsr and user mode registers are saved and restored in the iframe by exceptions.S */
mrc p15, 0, r2, c13, c0, 2
mrc p15, 0, r3, c13, c0, 3
push { r2-r11, lr }
/* save old sp */
str sp, [r0]
/* clear any exlusive locks that the old thread holds */
#if ARM_ARCH_LEVEL >= 7
/* can clear it directly */
clrex
#elif ARM_ARCH_LEVEL == 6
/* have to do a fake strex to clear it */
ldr r0, =strex_spot
strex r3, r2, [r0]
#endif
/* load new regs */
mov sp, r1
pop { r2-r11, lr }
mcr p15, 0, r2, c13, c0, 2
mcr p15, 0, r3, c13, c0, 3
bx lr
.ltorg
#if ARM_ARCH_LEVEL == 6
.data
strex_spot:
.word 0
#endif
.text
FUNCTION(arm_save_mode_regs)
mrs r1, cpsr
stmia r0, { r13, r14 }^ /* usr */
add r0, #8
cps #0x11 /* fiq */
str r13, [r0], #4
str r14, [r0], #4
cps #0x12 /* irq */
str r13, [r0], #4
str r14, [r0], #4
cps #0x13 /* svc */
str r13, [r0], #4
str r14, [r0], #4
cps #0x17 /* abt */
str r13, [r0], #4
str r14, [r0], #4
cps #0x1b /* und */
str r13, [r0], #4
str r14, [r0], #4
cps #0x1f /* sys */
str r13, [r0], #4
str r14, [r0], #4
msr cpsr_c, r1
bx lr
.text
/* void arm_chain_load(paddr_t entry, ulong arg0, ulong arg1, ulong arg2, ulong arg3) __NO_RETURN; */
/* shut down the system, branching into the secondary system */
FUNCTION(arm_chain_load)
/* shuffle the args around */
mov r4, r0 /* r4 = entry point */
mov r0, r1
mov r1, r2
mov r2, r3
ldr r3, [sp]
#if WITH_KERNEL_VM
/* The MMU is initialized and running at this point, so we'll need to
* make sure we can disable it and continue to run. The caller should
* have built a identity map for us and branched to our identity mapping,
* so it will be safe to just disable the mmu and branch to the entry
* point in physical space.
*/
/* Read SCTLR */
mrc p15, 0, r12, c1, c0, 0
/* Turn off the MMU */
bic r12, #0x1
/* Write back SCTLR */
mcr p15, 0, r12, c1, c0, 0
isb
#endif // WITH_KERNEL_VM
/* call the entry point */
bx r4
+401
Ver Arquivo
@@ -0,0 +1,401 @@
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <asm.h>
#include <arch/ops.h>
#include <arch/defines.h>
.text
#if ARM_WITH_CACHE
/* low level cache routines for various cpu families */
#if ARM_CPU_ARM1136
/* void arch_disable_cache(uint flags) */
FUNCTION(arch_disable_cache)
mov r12, #0 // zero register
mrs r3, cpsr // save the old interrupt state
cpsid iaf // interrupts disabled
.Ldcache_disable:
tst r0, #DCACHE
beq .Licache_disable
mrc p15, 0, r1, c1, c0, 0 // cr1
tst r1, #(1<<2) // is the dcache already disabled?
beq .Licache_disable
bic r1, #(1<<2)
mcr p15, 0, r1, c1, c0, 0 // disable dcache
mcr p15, 0, r12, c7, c14, 0 // clean & invalidate dcache
mcr p15, 0, r0, c7, c10, 4 // data sync barrier (formerly drain write buffer)
.Licache_disable:
tst r0, #ICACHE
beq .Ldone_disable
mrc p15, 0, r1, c1, c0, 0 // cr1
bic r1, #(1<<12)
mcr p15, 0, r1, c1, c0, 0 // disable icache
mcr p15, 0, r12, c7, c5, 0 // invalidate icache
.Ldone_disable:
msr cpsr, r3
bx lr
/* void arch_enable_cache(uint flags) */
FUNCTION(arch_enable_cache)
mov r12, #0 // zero register
mrs r3, cpsr // save the old interrupt state
cpsid iaf // interrupts disabled
.Ldcache_enable:
tst r0, #DCACHE
beq .Licache_enable
mrc p15, 0, r1, c1, c0, 0 // cr1
tst r1, #(1<<2) // is the dcache already enabled?
bne .Licache_enable
mcr p15, 0, r12, c7, c6, 0 // invalidate dcache
orr r1, #(1<<2)
mcr p15, 0, r1, c1, c0, 0 // enable dcache
.Licache_enable:
tst r0, #ICACHE
beq .Ldone_enable
mcr p15, 0, r12, c7, c5, 0 // invalidate icache
mrc p15, 0, r1, c1, c0, 0 // cr1
orr r1, #(1<<12)
mcr p15, 0, r1, c1, c0, 0 // enable icache
.Ldone_enable:
msr cpsr, r3
bx lr
#elif ARM_ISA_ARMV7
/* void arch_disable_cache(uint flags) */
FUNCTION(arch_disable_cache)
stmfd sp!, {r4-r11, lr}
mov r7, r0 // save flags
mrs r8, cpsr // save the old interrupt state
cpsid iaf // interrupts disabled
.Ldcache_disable:
tst r7, #DCACHE
beq .Licache_disable
mrc p15, 0, r0, c1, c0, 0 // cr1
tst r0, #(1<<2) // is the dcache already disabled?
beq .Ldcache_already_disabled
bic r0, #(1<<2)
mcr p15, 0, r0, c1, c0, 0 // disable dcache
// flush and invalidate the dcache
// NOTE: trashes a bunch of registers, can't be spilling stuff to the stack
bl flush_invalidate_cache_v7
b .Ldcache_disable_L2
.Ldcache_already_disabled:
// make sure all of the caches are invalidated
// NOTE: trashes a bunch of registers, can't be spilling stuff to the stack
bl invalidate_cache_v7
.Ldcache_disable_L2:
#if ARM_WITH_L2
// disable the L2, if present
mrc p15, 0, r0, c1, c0, 1 // aux cr1
bic r0, #(1<<1)
mcr p15, 0, r0, c1, c0, 1 // disable L2 dcache
#endif
.Licache_disable:
tst r7, #ICACHE
beq .Ldone_disable
mrc p15, 0, r0, c1, c0, 0 // cr1
bic r0, #(1<<12)
mcr p15, 0, r0, c1, c0, 0 // disable icache
.Ldone_disable:
// make sure the icache is always invalidated
mov r0, #0
mcr p15, 0, r0, c7, c5, 0 // invalidate icache to PoU
msr cpsr, r8
ldmfd sp!, {r4-r11, pc}
/* void arch_enable_cache(uint flags) */
FUNCTION(arch_enable_cache)
stmfd sp!, {r4-r12, lr}
mov r7, r0 // save flags
mrs r8, cpsr // save the old interrupt state
cpsid iaf // interrupts disabled
.Ldcache_enable:
tst r7, #DCACHE
beq .Licache_enable
mrc p15, 0, r0, c1, c0, 0 // cr1
tst r0, #(1<<2) // is the dcache already enabled?
bne .Licache_enable
// invalidate L1 and L2
// NOTE: trashes a bunch of registers, can't be spilling stuff to the stack
bl invalidate_cache_v7
#if ARM_WITH_L2
// enable the L2, if present
mrc p15, 0, r0, c1, c0, 1 // aux cr1
orr r0, #(1<<1)
mcr p15, 0, r0, c1, c0, 1 // enable L2 dcache
#endif
mrc p15, 0, r0, c1, c0, 0 // cr1
orr r0, #(1<<2)
mcr p15, 0, r0, c1, c0, 0 // enable dcache
.Licache_enable:
tst r7, #ICACHE
beq .Ldone_enable
mov r0, #0
mcr p15, 0, r0, c7, c5, 0 // invalidate icache to PoU
mrc p15, 0, r0, c1, c0, 0 // cr1
orr r0, #(1<<12)
mcr p15, 0, r0, c1, c0, 0 // enable icache
.Ldone_enable:
isb
msr cpsr, r8
ldmfd sp!, {r4-r12, pc}
// flush & invalidate cache routine, trashes r0-r6, r9-r11
flush_invalidate_cache_v7:
/* from ARMv7 manual, B2-17 */
dmb
MRC p15, 1, R0, c0, c0, 1 // Read CLIDR
ANDS R3, R0, #0x7000000
MOV R3, R3, LSR #23 // Cache level value (naturally aligned)
BEQ .Lfinished
MOV R10, #0
.Loop1:
ADD R2, R10, R10, LSR #1 // Work out 3xcachelevel
MOV R1, R0, LSR R2 // bottom 3 bits are the Cache type for this level
AND R1, R1, #7 // get those 3 bits alone
CMP R1, #2
BLT .Lskip // no cache or only instruction cache at this level
MCR p15, 2, R10, c0, c0, 0 // write the Cache Size selection register
isb // ISB to sync the change to the CacheSizeID reg
MRC p15, 1, R1, c0, c0, 0 // reads current Cache Size ID register
AND R2, R1, #0x7 // extract the line length field
ADD R2, R2, #4 // add 4 for the line length offset (log2 16 bytes)
LDR R4, =0x3FF
ANDS R4, R4, R1, LSR #3 // R4 is the max number on the way size (right aligned)
CLZ R5, R4 // R5 is the bit position of the way size increment
LDR R6, =0x00007FFF
ANDS R6, R6, R1, LSR #13 // R6 is the max number of the index size (right aligned)
.Loop2:
MOV R9, R4 // R9 working copy of the max way size (right aligned)
.Loop3:
ORR R11, R10, R9, LSL R5 // factor in the way number and cache number into R11
ORR R11, R11, R6, LSL R2 // factor in the index number
MCR p15, 0, R11, c7, c14, 2 // clean & invalidate by set/way
SUBS R9, R9, #1 // decrement the way number
BGE .Loop3
SUBS R6, R6, #1 // decrement the index
BGE .Loop2
.Lskip:
ADD R10, R10, #2 // increment the cache number
CMP R3, R10
BGT .Loop1
.Lfinished:
mov r10, #0
mcr p15, 2, r10, c0, c0, 0 // select cache level 0
dsb
isb
bx lr
// invalidate cache routine, trashes r0-r6, r9-r11
invalidate_cache_v7:
/* from ARMv7 manual, B2-17 */
dmb
MRC p15, 1, R0, c0, c0, 1 // Read CLIDR
ANDS R3, R0, #0x7000000
MOV R3, R3, LSR #23 // Cache level value (naturally aligned)
BEQ .Lfinished_invalidate
MOV R10, #0
.Loop1_invalidate:
ADD R2, R10, R10, LSR #1 // Work out 3xcachelevel
MOV R1, R0, LSR R2 // bottom 3 bits are the Cache type for this level
AND R1, R1, #7 // get those 3 bits alone
CMP R1, #2
BLT .Lskip_invalidate // no cache or only instruction cache at this level
MCR p15, 2, R10, c0, c0, 0 // write the Cache Size selection register
isb // ISB to sync the change to the CacheSizeID reg
MRC p15, 1, R1, c0, c0, 0 // reads current Cache Size ID register
AND R2, R1, #0x7 // extract the line length field
ADD R2, R2, #4 // add 4 for the line length offset (log2 16 bytes)
LDR R4, =0x3FF
ANDS R4, R4, R1, LSR #3 // R4 is the max number on the way size (right aligned)
CLZ R5, R4 // R5 is the bit position of the way size increment
LDR R6, =0x00007FFF
ANDS R6, R6, R1, LSR #13 // R6 is the max number of the index size (right aligned)
.Loop2_invalidate:
MOV R9, R4 // R9 working copy of the max way size (right aligned)
.Loop3_invalidate:
ORR R11, R10, R9, LSL R5 // factor in the way number and cache number into R11
ORR R11, R11, R6, LSL R2 // factor in the index number
MCR p15, 0, R11, c7, c6, 2 // invalidate by set/way
SUBS R9, R9, #1 // decrement the way number
BGE .Loop3_invalidate
SUBS R6, R6, #1 // decrement the index
BGE .Loop2_invalidate
.Lskip_invalidate:
ADD R10, R10, #2 // increment the cache number
CMP R3, R10
BGT .Loop1_invalidate
.Lfinished_invalidate:
dsb
mov r10, #0
mcr p15, 2, r10, c0, c0, 0 // select cache level 0
isb
bx lr
#else
#error unhandled cpu
#endif
#if ARM_CPU_ARM926 || ARM_CPU_ARM1136 || ARM_ISA_ARMV7
/* shared cache flush routines */
/* void arch_flush_cache_range(addr_t start, size_t len); */
FUNCTION(arch_clean_cache_range)
#if ARM_WITH_CP15
mov r3, r0 // save the start address
add r2, r0, r1 // calculate the end address
bic r0, #(CACHE_LINE-1) // align the start with a cache line
0:
mcr p15, 0, r0, c7, c10, 1 // clean cache to PoC by MVA
add r0, #CACHE_LINE
cmp r0, r2
blo 0b
#if ARM_ISA_ARMV7
dsb
#else
mov r0, #0
mcr p15, 0, r0, c7, c10, 4 // data sync barrier
#endif
#endif
#if WITH_DEV_CACHE_PL310
mov r0, r3 // put the start address back
b pl310_clean_range
#else
bx lr
#endif
/* void arch_flush_invalidate_cache_range(addr_t start, size_t len); */
FUNCTION(arch_clean_invalidate_cache_range)
#if ARM_WITH_CP15
mov r3, r0 // save the start address
add r2, r0, r1 // calculate the end address
bic r0, #(CACHE_LINE-1) // align the start with a cache line
0:
mcr p15, 0, r0, c7, c14, 1 // clean & invalidate dcache to PoC by MVA
add r0, r0, #CACHE_LINE
cmp r0, r2
blo 0b
#if ARM_ISA_ARMV7
dsb
#else
mov r0, #0
mcr p15, 0, r0, c7, c10, 4 // data sync barrier
#endif
#endif
#if WITH_DEV_CACHE_PL310
mov r0, r3 // put the start address back
b pl310_clean_invalidate_range
#else
bx lr
#endif
/* void arch_invalidate_cache_range(addr_t start, size_t len); */
FUNCTION(arch_invalidate_cache_range)
#if ARM_WITH_CP15
mov r3, r0 // save the start address
add r2, r0, r1 // calculate the end address
bic r0, #(CACHE_LINE-1) // align the start with a cache line
0:
mcr p15, 0, r0, c7, c6, 1 // invalidate dcache to PoC by MVA
add r0, r0, #CACHE_LINE
cmp r0, r2
blo 0b
#if ARM_ISA_ARMV7
dsb
#else
mov r0, #0
mcr p15, 0, r0, c7, c10, 4 // data sync barrier
#endif
#endif
#if WITH_DEV_CACHE_PL310
mov r0, r3 // put the start address back
b pl310_invalidate_range
#else
bx lr
#endif
/* void arch_sync_cache_range(addr_t start, size_t len); */
FUNCTION(arch_sync_cache_range)
push { r14 }
bl arch_clean_cache_range
mov r0, #0
mcr p15, 0, r0, c7, c5, 0 // invalidate icache to PoU
pop { pc }
#endif // ARM_CPU_...
#else
/* no cache */
FUNCTION(arch_disable_cache)
bx lr
FUNCTION(arch_enable_cache)
bx lr
FUNCTION(arch_clean_cache_range)
bx lr
FUNCTION(arch_clean_invalidate_cache_range)
bx lr
FUNCTION(arch_sync_cache_range)
bx lr
#endif // ARM_WITH_CACHE

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais