[magenta] Initial commit
Esse commit está contido em:
@@ -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']
|
||||
@@ -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)))
|
||||
)
|
||||
@@ -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
@@ -0,0 +1,2 @@
|
||||
# The location of the build configuration file.
|
||||
buildconfig = "//gnbuild/BUILDCONFIG.gn"
|
||||
@@ -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
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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()`.
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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()`.
|
||||
@@ -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" ]
|
||||
}
|
||||
@@ -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, "*")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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",
|
||||
]
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
Referência em uma Nova Issue
Bloquear um usuário