151 Commits

Author SHA1 Message Date
Anup Patel
48f91ee9c9 include: Bump-up version to 1.0
This patch updates OpenSBI version to 1.0 as part of
release preparation.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
2021-12-24 17:27:44 +05:30
Anup Patel
d30bde36d5 firmware: Move memcpy/memset mapping to fw_base.S
Some of the external firmwares using OpenSBI as library are facing
issues with the weak memcpy() and memset() aliases in libsbi.a so
we move these to fw_base.S. This way mapping of implicit memcpy()
or memset() calls to sbi_memcpy() or sbi_memset() will only be done
for OpenSBI firmwares.
(Refer, https://github.com/riscv-software-src/opensbi/issues/234)

In addition, we also add memmove() and memcmp() mappings in fw_base.S
because as-per the GCC documentation the freestanding environment must
provide memcpy(), memmove(), memset(), and memcmp().

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atishp@rivosinc.com>
2021-12-23 17:46:09 +05:30
Heinrich Schuchardt
2082153fc9 lib: sbi: simplify pmp_set(), pmp_get()
pmpcfg_csr and pmpcfg_shift are only negative for an unexpected value of
__riscv_xlen. We can immediately return in this case.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-12-22 09:05:05 +05:30
Xiang W
f270359810 Makefile: Improve the method to disable -m(no-)save-restore option
The commit 69d7e53 disables the -m(no-)save-restore option for
clang, but clang11 supports this option. This patch uses the
output information of the compiler to check whether the compiler
supports this option.

Signed-off-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-12-16 09:27:22 +05:30
Anup Patel
d249d6544c lib: sbi: Fix compile errors using -Os option
When compiling with -Os option along with -ffreestanding, both GCC
and clang will add implicit calls to memcpy() and memset() for stack
variables initialized in declaration.

The C standard as per Clause 4, the compiler cannot necessarily
assume that anything beyond:

 * float.h
 * iso646.h
 * limits.h
 * stdalign.h
 * stdarg.h
 * stdbool.h
 * stddef.h
 * stdint.h
 * stdnoreturn.h
 * fenv.h
 * math.h
 * and the numeric conversion functions of stdlib.h.

This patch maps memcpy() and memset() as weak-alias of sbi_memcpy()
and sbi_memset() respectively so that implicit calls to memcpy()
and memset() will compile properly.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-12-11 17:37:29 +05:30
Anup Patel
69d7e53613 Makefile: Fix -msave-restore compile warning with CLANG-10 (or lower)
The riscv target of CLANG-10 (or lower) does not support the
-m(no-)save-restore option so we get compile warnings. This patch
fixes compile warning by using -m(no-)save-restore option only
for GCC.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
2021-12-11 17:37:26 +05:30
Nikita Shubin
460041c816 lib: pmu: check SSCOF before masking
We should check if SSCOF extension is present,
before applying inhibit mask and clearing overflow,
otherwise undesirable value can be written
in MHPMEVENT_N CSR.

Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-12-11 16:26:15 +05:30
Tsukasa OI
66fbcc03df docs/platform: spike: Enhance Spike examples
This commit makes Spike usable as QEMU (in fact, those are based on
QEMU examples).

Signed-off-by: Tsukasa OI <research_trasio@irq.a4lg.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-12-11 16:24:39 +05:30
Vincent Chen
2428987cc0 lib: pmu: support the event ID encoded by a bitmap.
RISC-V privilege specification does not specify how to encode the event ID.
Therefore, each platform is allowed to customize its own encoding rule.
The common encoding methods are as follow, directly assigning a number to an
event, or every bit in the mphmevent CSR controls one specified event or
mixes the above two methods.

To enable OpenSBI to support the above three encoding methods simultaneously,
this patch repurpose the dt property "riscv,raw-event-to-mhpmcounters". The
"riscv,raw-event-to-mhpmcounters" will describes the one or multiple raw
events that could be counted by a set of counters. But, the column number
of "riscv,raw-event-to-mhpmcounters" is extended from 2 to 3. The 1st column
(64bit) is the ID of the raw events. The 2nd column (64bit) represents a
select_mask now to represent the bits used for event ID encoding.
If a platform directly encodes each raw PMU event as a unique ID,
the value of select_mask will be 0xffffffff_ffffffff.

Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
Signed-off-by: Atish Patra<atishp@rivosinc.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-12-03 09:37:03 +05:30
Jessica Clarke
14faee6916 lib: sbi: Improve fatal error handling
BUG and BUG_ON are not informative and are rather lazy interfaces, only
telling the user that something went wrong in a given function, but not
what, requiring the user to find the sources corresponding to their
firmware (which may not be available) and figure out how that BUG(_ON)
was hit. Even SBI_ASSERT in its current form, which does include the
condition that triggered it in the output, isn't necessarily very
informative. In some cases, the error may be fixable by the user, but
they need to know the problem in order to have any hope of fixing it.
It's also a nuisance for developers, whose development trees may have
changed significantly since the release in question being used, and so
line numbers can make it harder for them to understand which error case
a user has hit.

This patch introduces a new sbi_panic function which is printf-like,
allowing detailed error messages to be printed to the console. BUG and
BUG_ON are removed, since the former is just a worse form of sbi_panic
and the latter is a worse version of SBI_ASSERT. Finally, SBI_ASSERT is
augmented to take a set of arguments to pass to sbi_panic on failure,
used like so (sbi_boot_print_hart's current error case, which currently
manually calls sbi_printf and sbi_hart_hang):

  SBI_ASSERT(xlen >= 1, ("Error %d getting MISA XLEN\n", xlen));

The existing users of BUG are replaced with calls to sbi_panic along
with informative error messages. BUG_ON and SBI_ASSERT were unused (and,
in the case of SBI_ASSERT, remain unused).

Many existing users of sbi_hart_hang should be converted to use either
sbi_panic or SBI_ASSERT after this commit.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Atish Patra <atishp@rivosinc.com>
2021-12-02 08:50:36 +05:30
Dong Du
7e77706966 lib: sbi: Resolve the uninitialized complaint in sbi_pmu
A recent commit (b28f0700) turns to read from CSR_MCOUNTINHIBIT after
checking the SBI_HART_HAS_MCOUNTINHIBIT HART feature, which leaves the
mctr_inhbt uninitialized in pmu_ctr_find_hw().
As a result, compiler will complain:
error: 'mctr_inhbt' may be used uninitialized in this function.

This commit resolves the issue by assigning an initial value to mctr_inhbt.

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-30 10:40:35 +05:30
Nikita Shubin
7a22c780df lib: sbi: Fix missing space
Fix missing space in sbi_hart_pmp_configure
error printing.

Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Xiang w <wxjstz@126.com>
2021-11-30 10:35:34 +05:30
Vagrant Cascadian
22d556d268 lib: sbi: Fix spelling of "address" in sbi_domain.c
Fix a spelling typo in error print.

Signed-off-by: Vagrant Cascadian <vagrant@debian.org>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-11-23 17:52:13 +05:30
Gabriel Somlo
52af6e4b52 lib: utils: Add LiteX UART support
Add support for the UART provided by the LiteX SoC
framework (https://github.com/enjoy-digital/litex),
based on its FDT info (described in the Linux tree at
Documentation/devicetree/bindings/serial/litex,liteuart.yaml).

Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-18 13:22:02 +05:30
Xiang W
d335a178d8 lib: sbi: clear pmpcfg.A before setting in pmp_set()
We should clear A bits in prot variable before enabling A_NA4 or A_NAPOT.

Signed-off-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-12 12:19:00 +05:30
Nikita Shubin
f3744967c6 platform: sifive_fu740: add platform reset driver
da9063 PMIC can be used to reset/shutdown the
Sifive Unmatched board.

shutdown is done simply by writing SHUTDOWN bit to
DA9063_REG_CONTROL_F register.

reset via setting WAKEUP bit in DA9063_REG_CONTROL_F
register followed by masking POWER and POWER1 domains
and setting STANDBY bit in DA9063_REG_CONTROL_A,
originally discovered by Alexandre Ghiti on linux-riscv
maillists.

Tested-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Tested-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
2021-11-12 10:29:20 +05:30
Nikita Shubin
13a1158d24 lib: utils/i2c: Add minimal SiFive I2C driver
Minimum SiFive I2C driver to read/send bytes over I2C bus.

This allows querying information and perform operation of onboard PMIC,
as well as power-off and reset.

Tested-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Tested-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
2021-11-12 10:29:08 +05:30
Nikita Shubin
6ca6bcafb7 lib: utils/i2c: Add simple FDT based I2C framework
FDT based I2C framework on the top of I2C library.

The drivers are probed on demand by fdt_i2c_adapter_get
function.

Tested-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Tested-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
2021-11-12 10:19:45 +05:30
Nikita Shubin
2c964a2e15 lib: utils/i2c: Add generic I2C configuration library
Helper library to keep track of registered I2C adapters,
identified by dts offset, basic send/read functions and
adapter configuration (enable, set dividers, etc...).

Tested-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Tested-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
2021-11-12 10:17:50 +05:30
Nikita Shubin
1d462e0397 lib: utils/reset: separate driver init func
Move driver init code to separate function, so it can be reused
elsewhere.

Tested-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Tested-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
2021-11-12 10:16:23 +05:30
Nikita Shubin
94eba23376 lib: utils/reset: add priority to gpio reset
Make gpio_system_reset_check return priority instead of just true/false.

Make default 128 priority for reset/shutdown.

Tested-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Tested-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
2021-11-12 10:15:54 +05:30
Vasan VS
31fe5a7a3d lib: sbi: Fix PMP address bits detection
We should ensure that pmpcfg0.pmp0cfg is set to zero before using
pmpaddr0 CSR for detecting implemented PMP address bits.

Fixes: bf21632860 ("lib: sbi: Detect PMP granularity and number
of address bits")
Signed-off-by: Vasan VS <vasan.vs@gmail.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
2021-11-11 18:27:53 +05:30
Atish Patra
b8845e4204 lib: sbi: Fix initial value mask while updating the counters
The first 32 bits of the initial value for the counter should be
preserved while updating the mhpmcounter for 32bit.

Fixes: 13d40f21d5 ("lib: sbi: Add PMU support")
Signed-off-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-11 17:57:17 +05:30
Atish Patra
15906a3984 lib: utils: Rename the prefix in PMU DT properties
As per the DT schema rules, the prefix should be vendor. As the PMU
properties are generic for all vendors, change the prefix to riscv
instead of pmu.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:54:46 +05:30
Atish Patra
b28f07005c lib: sbi: Enable PMU extension for platforms without mcountinhibit
Some platforms such as hifive unmatched doesn't implement mcountinhibit
csr. However, it has hardware events that can be monitored using 2
hpmcounter it has (i.e. mhpmcounter3 & mhpmcounter4).

Currently, PMU extension disabled if mcountinhibit is absent. That's not
really necessary as long as the supervisor OS keeps track of the delta
value of the counters. Without mcountinhibit, the delta value won't be
entirely accurate because the counters are freely running. However, that
should be fine to produce an approximate counter value which can help
performance analysis. Perf sampling won't work though as sscof extension
is not present in hifive unmatched.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:54:01 +05:30
Atish Patra
b628cfd6a0 lib: sbi: Counter info width should be zero indexed
The mhpm bits represent the number of bits available in mhpmcounter
while counter width describes a zero indexed value. Fix the counter width
calculation.

Fixes: 13d40f21d5 ("lib: sbi: Add PMU support")
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:52:19 +05:30
Atish Patra
1e147324f0 lib: sbi: Reset the mhpmevent value upon counter reset
The hardware solely relies on the event selector value in mhpmevent
to figure out what event to monitor using that counter. It should be
reset when counter reset happens.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:50:32 +05:30
Atish Patra
0c304b6619 lib: sbi: Allow programmable counters to monitor cycle/instret events
A platform may use programmable counters for cycle/instret events.
The priv spec allows that provided that cycle/instret also report those
events in addition to the programmable counters. We should allow that
functionality in OpenSBI.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:48:45 +05:30
Atish Patra
2363f950bc lib: sbi: Always enable access for all counters
OpenSBI doesn't use any counters for its own usage. Thus, all the counters
can be made accessible for lower privilege mode always. However, the
mcountinhibit must be set so that the counter doesn't increment.
As a result, we don't have to enable/disable mcounteren at every start/stop.

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:47:52 +05:30
Atish Patra
730f01bb41 lib: sbi: Support sscofpmf extension in OpenSBI
This patch adds sscofpmf extension in pmu module which includes
following things.

1. Enable overflow irq when starting a counter.
2. Setting the correct event filters passed from supervisor.
3. Delegating the overflow interrupt to the supervisor.
4. Add RV32 support for sscofpmf.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:46:57 +05:30
Atish Patra
9134c3643e lib: sbi: Delegate PMU counter overflow interrupt to S mode
OpenSBI doesn't handle PMU counters for now.

Delegate the overflow counter to S-mode always.

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:45:12 +05:30
Atish Patra
867c65360d lib: sbi: Detect Sscofpmf extension at run time
Sscofpmf ISA extension introduces PMU counter overflow and filtering support.
It introduces a read only `scountovf` csr that can be used to detect if
a hart supports this extension at runtime. However, this feature is only
useful if the hart already supports mcounteren and mcountinhibit.

Add a dynamic detection mechanism and boot time print message if sscofpmf
is present.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:42:59 +05:30
Atish Patra
7084ad9f42 lib: sbi: Update csr_read/write_num for PMU
The Sscofpmf extension introduces mhpmevent[h] csrs to handle filtering
/overflow bits in RV32. There is no way to read/write mcountinhibit
using mcountinhibit csr using a variable.

Updated the support to read/write mhpmevent[h] and mcountinhibit csr.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:39:38 +05:30
Atish Patra
a74daf2cb9 riscv: Add new CSRs introduced by Sscofpmf[1] extension
[1] https://drive.google.com/file/d/1KcjgbLM5L1ZKY8934aJl8aQwGlMz6Cbo/view

Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-11-11 17:37:34 +05:30
Heinrich Schuchardt
be245acfff lib: sbi: error handling in fdt_reset_init()
The initialization of a reset driver may fail for various reasons, like
a PMIC based reset driver not finding the required I2C driver. The return
code of the init routine may take other error values than -ENODEV.

If the initialization of a reset driver fails, this should not lead to the
board hanging. It is enough that the reset driver does not call
sbi_system_reset_add_device() to avoid invoking the driver for a device
that could not be initialized.

Change the return type of fdt_reset_init() to void.
Print a message if an error occurs.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-08 10:50:48 +05:30
Heinrich Schuchardt
57f094e67e platform: generic: move fdt_reset_init to final_init
Move the fdt_reset_init() invocation from generic_early_init() to
generic_final_init(). This allows to print error messages.

Ignore the return value of fdt_reset_init() as we should not stop booting
due to failure to initialize reset drivers.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-08 10:42:05 +05:30
Heinrich Schuchardt
2fe2f55d50 lib: sbi: move sbi_boot_print_general()
Moving the sbi_boot_print_general() call after the
sbi_platform_final_init() call allows to print devices initialized in the
latter.

To keep the overall print sequence the same also move
sbi_boot_print_domains().

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-08 10:39:31 +05:30
Nikita Shubin
0979ffda12 lib: utils/gpio: use list for drivers
Convert static array to sbi_list.

This removes size limitation, makes add/remove more efficient and
saves space.

Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-03 15:57:56 +05:30
Anup Patel
013ba4ef3d lib: sbi: Fix GPA passed to __sbi_hfence_gvma_xyz() functions
The parameter passed to HFENCE.GVMA instruction in rs1 register
is guest physical address right shifted by 2 (i.e. divided by 4).

Unfortunately, we overlooked the semantics of rs1 registers for
HFENCE.GVMA instruction and never right shifted guest physical
address by 2. This issue did not manifest for hypervisors till
now because all H-extension implementations (such as QEMU, Spike,
Rocket Core FPGA, etc) we tried till now were conservatively
flushing everything upon any HFENCE.GVMA instruction.

This patch fixes GPA passed to __sbi_hfence_gvma_vmid_gpa()
and __sbi_hfence_gvma_gpa() functions.

Fixes: 331ff6a162 ("lib: Support stage1 and stage2 tlb flushing")
Reported-by: Ian Huang <ihuang@ventanamicro.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
2021-11-02 16:02:08 +05:30
Alexandre Ghiti
c891acca17 include: sbi_utils: Introduce an helper to get fdt base address
This simply adds an helper to get fdt address which is more explicit than
sbi_scratch_thishart_arg1_ptr.

Signed-off-by: Alexandre Ghiti <alexandre.ghiti@canonical.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-02 11:23:16 +05:30
Dong Du
723aa88ff4 lib: sbi: Refine addr format in sbi_printf
Although we have PRILX to help us print unsigned long without
considering the 32bit/64bit differences, there are still some
places using 08lx and 016lx manually --- leading to redundant code.

This commit fixes the issue by using PRILX all the time.

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-11-02 10:42:01 +05:30
Anup Patel
309e8bdf85 lib: utils/reset: Register separate GPIO system reset devices
Now that sbi_system support multiple system reset devices, we should
register separate devices for GPIO restart and GPIO poweroff because
DT describes these as separate devices with different compatible
strings.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
2021-11-02 09:14:11 +05:30
Samuel Holland
78c2b19218 lib: utils/irqchip: Automatically delegate T-HEAD PLIC access
The T-HEAD PLIC implementation requires setting a delegation bit
to allow access from S-mode. Now that the T-HEAD PLIC has its own
compatible string, set this bit automatically from the PLIC driver,
instead of reaching into the PLIC's MMIO space from another driver.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-10-21 10:06:00 +05:30
Wei Fu
422eda499c Makefile: Add build time and compiler info string
When we are doing opensbi development, we want to know the build time
and compiler info for debug purpose.

To enable this message, please add "BUILD_INFO=y", like:

```
make BUILD_INFO=y
```

NOTE: Using `BUILD_INFO=y` without specifying SOURCE_DATE_EPOCH will
violate "reproducible builds". So it's ONLY for development and debug
purpose, and should NOT be used in a product which follows "reproducible
builds".

Signed-off-by: Wei Fu <wefu@redhat.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
2021-10-20 15:10:52 +05:30
Heinrich Schuchardt
67cbbcb100 lib: sbi: system reset with invalid parameters
The SBI specification requires that sbi_system_reset() returns
SBI_ERR_INVALID_PARAM if reset_type or reset_reason are not valid.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-10-18 16:07:21 +05:30
Anup Patel
c38973e087 lib: sbi: Save context for all non-retentive suspend types
Instead of saving context only for default non-retentive suspend,
we should save context for all non-retentive suspend types.

Fixes: 74756891cc ("lib: sbi: Implement SBI HSM suspend function")
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-10-18 16:07:16 +05:30
Nikita Shubin
9283d503bd lib: sbi: add priority for reset handler
Let's make system_reset_check returning priority instead of only
true/false. In that case 0 - means not supported, and anything above
means priority that makes existing reset handlers being used in first
place, unless it is decided to lower their priority.

The handler with the most priority wins.

Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-10-11 10:32:26 +05:30
Nikita Shubin
516161c46f lib: sbi: convert reset to list
To support different handlers for different types of resets, we are
adding a sbi_list of restart handlers.

Instead of sbi_system_reset_set_device we use
sbi_system_reset_add_device to reflect the actual meaning.

Signed-off-by: Nikita Shubin <n.shubin@yadro.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-10-11 09:59:29 +05:30
Heinrich Schuchardt
754d51192b lib: utils: identify supported GPIO reset methods
The GPIO reset driver supports reset and poweroff. But not all boards
support both. gpio_system_reset_check() must detect this situation.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-10-01 10:24:43 +05:30
Anup Patel
fa59dd317a lib: utils/reset: use sbi_timer_mdelay() in gpio reset driver
We should use sbi_timer_mdelay() instead of custom gpio_mdelay() in
the gpio reset driver.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-09-26 19:52:18 +05:30
Anup Patel
9d0ab35ab4 lib: sbi: Add generic timer delay loop function
We now have frequency of the timer device provided by the platform
support so we can emulate desired delay using a loop where the number
loop iterations are based on timer frequency.

This patch provides sbi_timer_delay_loop() for above purpose.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-09-26 19:52:15 +05:30
Anup Patel
6355155f51 lib: sbi: Print timer frequency at boot time
We now have frequency in timer device instance provided by platform
so let's print timer frequency as part of the boot prints.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-09-26 19:52:12 +05:30
Anup Patel
12e7af9ab7 lib: sbi: Add timer frequency to struct sbi_timer_device
Generic mdelay() and udelay() functions can be provided by the
sbi_timer framework if timer frequency is available in the timer
instance provided by the platform support or timer driver.

This patch adds timer frequency (timer_freq) member in the
struct sbi_timer_device for above purpose.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-09-26 19:52:10 +05:30
Anup Patel
72154f4708 lib: utils/fdt: Add fdt_parse_timebase_frequency() function
We add fdt_parse_timebase_frequency() function which can be used
by ACLINT mtimer driver and platform code to get timebase frequency.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-09-26 19:52:07 +05:30
Xiang W
51113fe2a5 lib: sbi: Add BUG() macro for csr_read/write_num() and misa_string()
We use BUG() macro in csr_read_num(), csr_write_num(), and
misa_string() functions for unhandled cases.

Signed-off-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-09-22 13:38:39 +05:30
Xiang W
12753d2256 lib: sbi: add some macros to detect BUG at runtime
Three macros are added. One is called BUG, which is used to put in an
unreachable branch. One is called BUG_ON, which is used to check bugs
and assert conditions are opposite. One is called SBI_ASSERT, used for
assertion checking.

Signed-off-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-09-22 13:35:30 +05:30
Xiang W
3477f08b08 lib: sbi: fix ctz bug
The original version of ctz will cause an endless loop, if the parameter
passed in is 0. This commit fixes this bug.

Signed-off-by: Xiang W <wxjstz@126.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-09-22 13:26:23 +05:30
Samuel Holland
395ff7eede lib: utils/reset: Add a sunxi watchdog reset driver
One of the watchdogs in the D1 SoC provides a "soft reset" function,
which allows software to immediately reset the entire SoC. Add a driver
so it can implement the SBI system reset function.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-09-14 13:24:53 +05:30
Samuel Holland
0274a96004 lib: utils/reset: Sort fdt_reset driver list
In preparation for adding a new fdt_reset driver, ensure the existing
lists are sorted alphabetically.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-09-14 12:13:55 +05:30
Dong Du
bd355213bf lib: sbi: Refine the way to construct platform features
sbi_platform_get_features_str() uses sbi_snprintf() to construct the
features_str. However, it passes the wrong length value (i.e., the nfstr),
which should be (nfstr-offset) as the starting point of str (i.e.,
features_str + offset) changes.

This commit also checks the return value of snprintf, and handles the
corner case that the string buffer is full.

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-09-03 12:16:01 +05:30
Dong Du
1718b1642e lib: sbi: Checking fifo validness in sbi_fifo_is_empty and is_full
As other exported fifo functions, we should check whether the fifo is
valid in sbi_fifo_is_empty and sbi_fifo_is_full. To this end, this patch
changes the retval from bool to int, and the two functions will return
SBI_EINVAL in the case the fifo is invalid.

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-09-03 11:59:35 +05:30
Dong Du
c262306533 lib: sbi: protect dprintf output with spinlock
Avoid getting messages from multiple harts (using dprintf and printf)
concurrently with a spinlock serializaing calls to sbi_dprintf(),
sbi_printf() and sbi_puts()

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-08-30 09:12:37 +05:30
Rahul Pathak
bd316e2c9b lib: sbi: Correct typo in faults delegation CSR name
Correcting the name of faults delegation CSR
%s/mfdeleg/medeleg

Signed-off-by: Rahul Pathak <rpathak@ventanamicro.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-08-30 09:07:47 +05:30
Samuel Holland
b1d3e91e9a payloads/test: Add support for SBI v0.2 ecalls
It can be useful to make SBI v0.2 or newer ecalls from this payload
for testing purposes. To support this, convert the macros to use the
extension/function parameter convention.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-20 09:48:45 +05:30
Samuel Holland
ee274377b2 lib: sbi_trap: Restore redirect for access faults
commit 764a17d852 ("lib: sbi: Implement firmware counters") added
switch cases for CAUSE_LOAD_ACCESS and CAUSE_STORE_ACCESS. This caused
them to stop being redirected to U or S mode, as that is handled in the
default switch case. As a result, an error in userspace could cause the
system to hang. Fix this by allowing the acces fault case to fall
through to the default case.

Fixes: 764a17d852 ("lib: sbi: Implement firmware counters")
Signed-off-by: Samuel Holland <samuel@sholland.org>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
2021-08-20 09:47:40 +05:30
Dong Du
33eac764f2 lib: sbi: Fix bug in sbi_ecall_rfence that misses checking
In sbi_ecall_rfence_handler, it will compare the funcid with
REMOTE_HFENCE_GVMA and REMOTE_HFENCE_VVMA_ASID. Later it check
whether the misa includes H-extension. This checking is incomplete
which misses REMOTE_HFENCE_GVMA_VMID and REMOTE_HFENCE_VVMA.

Fix the issue by updating the checking range.

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-20 09:42:39 +05:30
Anup Patel
7aa6c9aa96 lib: utils/timer: Simplify MTIMER synchronization
We simplify MTIMER synchronization as follows:

1) Detect MTIMER devices with unique (or non-shared) MTIME
   register at boot-time
2) Select first MTIMER device with no associated HART as our
   reference MTIMER device
3) Only synchronize MTIMER devices with unique (or non-shared)
   MTIME register using reference MTIMER device
4) Directly update the MTIME register at time of synchronization
   because MTIME is a read/write register.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-14 09:07:42 +05:30
Anup Patel
b35f7826b0 lib: utils/timer: Allow ACLINT MTIMER supporting only 32-bit MMIO
We can have ACLINT MTIMER devices which only support 32-bit MMIO
accesses on RV64 system so this patch adds a boolean DT property
"mtimer,no-64bit-mmio" to detect this from MTIMER DT node.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-14 09:02:44 +05:30
Anup Patel
f3a0eb8583 lib: utils/fdt: Extend fdt_parse_aclint_node() function
The fdt_parse_aclint_node() is used to parse DT node for SiFive
CLINT, ACLINT MTIMER, and ACLINT MSWI devices.

The ACLINT MTIMER has undergone following changes:
1) MTIMER DT node now requires separate addresses in for MTIME
   register and MTIMECMPx registers in the reg DT property.
2) MTIMER DT node might have no interrupts-extended DT property
   when the MTIMER device has no associated HARTs (i.e. the
   MTIMER device has no MTIMECMPx registers)

This patch extends fdt_parse_aclint_node() to handle above
mentioned changes in ACLINT MTIMER DT bindings.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-14 09:02:39 +05:30
Anup Patel
7a3a0cce4d lib: utils: Extend fdt_get_node_addr_size() for multiple register sets
We add "index" parameter to fdt_get_node_addr_size() API so that
calling function can specify index of desired register set. This
will allow fdt_get_node_addr_size() to handle DT nodes with
multiple register sets.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-14 09:02:36 +05:30
Anup Patel
e0d1b9db8a lib: utils/timer: Allow separate base addresses for MTIME and MTIMECMP
We extend the ACLINT library to support separate base addresses
for MTIME and MTIMECMP registers.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-08-14 09:02:33 +05:30
Bin Meng
47a47654e8 lib: utils/fdt: Change addr and size to uint64_t
The maximum address and size encoded in DT are 64-bit numbers, so we
should use uint64_t for 'addr' and 'size' in fdt_get_node_addr_size().

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-08-07 17:26:51 +05:30
Dong Du
d244f3dbd6 lib: sbi: Fix bug in strncmp function when count is 0
No need to compare characters when the count turns to 0.
Fix the issue in sbi_strncmp.

Signed-off-by: Dong Du <Dd_nirvana@sjtu.edu.cn>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-08-07 15:40:40 +05:30
Heinrich Schuchardt
e928472e67 lib: utils: support both of gpio-poweroff, gpio-reset
The generic GPIO reset driver has two entries in the match table:
"gpio-poweroff", "gpio-reset". Only the first entry is considered by
fdt_reset_init().

Define "gpio-poweroff" and "gpio-reset" as compatibility strings of two
separate reset drivers. They still can share code.

Fixes: e3d6919d10 ("lib: utils/reset: Add generic GPIO reset driver")
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-27 17:26:54 +05:30
Heinrich Schuchardt
ce03c88ee1 lib: utils: remove unused variable in fdt_reset_init
The value of variable current_driver is unused. Remove the variable.

Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-27 17:16:02 +05:30
David Abdurachmanov
217d5e4880 generic: fu740: add workaround for CIP-1200 errata
Instruction TLB can fail to respect a non-global SFENCE thus we need to
flush the TLB using SFENCE.VMA x0, x0

See full description of CIP-1200 in Errata_FU740-C000_20210205 from
https://www.sifive.com/boards/hifive-unmatched

Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
2021-07-27 15:29:59 +05:30
Xiang W
74db0acbe5 firmware: use _fw_start for load address
The previous code uses _start as the load address, this default .entry is
the first segment, using _fw_start does not need to make this assumption.

Signed-off-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-27 15:20:49 +05:30
Bin Meng
989039117f Makefile: Manually forward RELAX_FLAG to the assembler when linking with LLD
When generating code with -mno-relax, GCC puts .option norelax in
the generated assembly, and so doesn’t bother passing on -mno-relax
to the assembler. This has the unfortunate effect that, when using
GCC to assemble hand-written assembly, -mno-relax does nothing,
and we have to pass -Wa,-mno-relax to manually forward it to the
assembler.

This is an old GCC bug that was fixed [1] recently. For the time
being, let's pass "-Wa,-mno-relax" to ASFLAGS for the GCC + LLD
combination to work, e.g.:

  $ make CC=riscv64-unknown-elf-gcc LLVM=1 PLATFORM=generic

[1] 3b0a7d624e

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-27 14:29:31 +05:30
Bin Meng
81eb7088b6 README: Update toolchain information
Recent FW_PIC=y changes actually require toolchains with PIE support
and it is on by default. Existing doc uses a GNU bare-metal toolchain
as examples but it does not support PIE.

Replace references of bare-metal toolchain prefix with Linux toolchain
prefix everywhere in documentation.

Also indicate that "riscv64-unknown-freebsd-" as an alternative to
"riscv64-linux-" GNU toolchain.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-17 18:32:20 +05:30
Bin Meng
2c74dc3c47 docs: Document FW_PIC compile time option
FW_PIC=y is on by default, but the doc is missing when this was
introduced. Add some description for it.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-17 17:43:37 +05:30
Bin Meng
a4555e5698 docs: Document parameters passed to firmware and alignment requirement
This updates documentation to describe parameters passed to firmware
from previous booting stage, and corresponding address alignment
requirement.

This also fixes a typo in fw_dynamic.md (it's => its).

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-17 17:30:48 +05:30
Bin Meng
8a1475b5a1 firmware: Remove the unhelpful alignment codes before fdt relocation
If the device tree is at an address that is not __SIZEOF_POINTER__
aligned, the fdt relocation code tries to align both source and
destination address to __SIZEOF_POINTER__ before the memory copy.
But such alignment can lead to unexpected results if either source
or destination address is not aligned.

In fact libfdt requires that the device tree must be at an 8-byte
aligned address. Hence remove the unhelpful alignment codes.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-17 17:13:43 +05:30
Alex Richardson
f3a8f603a7 include: types: Use __builtin_offsetof when supported
Clang provides a __builtin_offsetof which can be detected using
__has_builtin().

Signed-off-by: Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-17 16:40:07 +05:30
Alex Richardson
7210e90785 firmware: use __SIZEOF_LONG__ for field offsets in fw_dynamic.h
The fields are of type unsigned long and are not pointers. While this
happens to be the same for RV32/RV64, it is not correct when compiling
for a CHERI-RISC-V system where pointers are twice the size of long.

Signed-off-by: Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-17 16:35:23 +05:30
Green Wan
e3d6919d10 lib: utils/reset: Add generic GPIO reset driver
We add generic GPIO reset driver inspired from gpio-restart
and gpio-poweroff drivers of Linux kernel.

Signed-off-by: Green Wan <green.wan@sifive.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-17 14:12:07 +05:30
Green Wan
4c3df2ab96 lib: utils/gpio: Add minimal SiFive GPIO driver
We add a minimal SiFive GPIO driver so that we can do GPIO based
system power-off and reset.

Signed-off-by: Green Wan <green.wan@sifive.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-17 13:56:42 +05:30
Anup Patel
c14f1fe0df lib: utils/gpio: Add simple FDT based GPIO framework
We add a simple FDT based GPIO framework which is built on top
of generic GPIO library. The phandle of FDT GPIO chip DT node
is treated as unique GPIO chip ID required by the generic GPIO
library. The FDT based GPIO chip drivers will be probed on-demand
from fdt_gpio_pin_get() called by the GPIO client drivers.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-17 13:41:17 +05:30
Anup Patel
36b8effe4a lib: utils/gpio: Add generic GPIO configuration library
We add generic GPIO configuration library which is independent of
hardware description format (FDT or ACPI). The OpenSBI platform
support or GPIO drivers can register GPIO chip instances which
can be discovered and used by different GPIO clients. Each GPIO
chip instance has a unique ID which can be used by GPIO clients
to lookup GPIO chip instance.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-17 11:42:51 +05:30
Anup Patel
e931f387b2 lib: utils/fdt: Add fdt_parse_phandle_with_args() API
The libfdt project does not have a generic API to parse phandle
with args from a DT node so we add fdt_parse_phandle_with_args()
for this purpose. This new API will be useful to FDT based drivers.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-17 11:40:49 +05:30
Jessica Clarke
17729d44da lib: utils: Drop dependency on libgcc by importing part of FreeBSD's libquad
We only need libgcc for 64-bit division on RV32. Whilst GCC toolchains
bundle libgcc, Clang toolchains tend not to ship libclang_rt.builtins
given every compiler is a cross-compiler for every target and so you
would need a silly number of builds of it, with only the native library
available; only vendor-provided Clang toolchains specifically for bare
metal cross-compiling are likely to provide it.

Thus, import part of FreeBSD's implementation of the division support
functions needed and stop linking against libgcc.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Tested-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 21:01:44 +05:30
Jessica Clarke
2942777425 Makefile: Support building with Clang and LLVM binutils
This is intended to mirror the Linux kernel. Building with CC=clang will
use Clang as the compiler but default to using the existing binutils.
Building with LLVM=1 will default to using Clang and LLVM binutils.

Whilst GCC will accept the -N linker option and forward it on to the
linker, Clang will not, and so in order to support both compilers we
must use -Wl, to forward it to the linker as is required for most other
linker options.

Note that there is currently a bug when using Clang as the compiler and
riscv64-linux-gnu-ld as the linker for FW_PIC=y. At first glance this
appears to be a bug in GNU binutils, but this could also be Clang or
OpenSBI at fault in some subtle way. Thus, for now, advise that this
combination be avoided.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Tested-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 20:22:55 +05:30
Jessica Clarke
09ad811ec4 firmware: Only default FW_PIC to y if supported
Bare-metal GNU ld does not support PIE, so if using it this will result
in a failure to build. Instead, default to FW_PIC=n if not supported.
Note that an explicit FW_PIC=y is not overridden, to ensure the build
fails rather than silently producing a position-dependent binary.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 20:03:06 +05:30
Jessica Clarke
a3d328ae33 firmware: Explicitly pass -pie to the linker, not just the driver
When using Clang with a bare-metal triple, -pie does not get passed to
the linker as it's not normally a thing that makes sense, unlike GCC
which will unconditionally forward it on and potentially result in a
linker error. However, LLD does support it, and manually forwarding it
on works as desired, so do so to fully support FW_PIC with Clang and
LLD.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 20:01:56 +05:30
Jessica Clarke
397afe5ba1 fw_base: Put data in .data rather than .text
The -N linker option is supposed to make .text writable, but GNU ld and
LLD differ in interpreting what that means. GNU ld will happily let you
have relocations in it, but LLD will see that the input section is
read-only (even though the output section is writable) and give an
error. It's unclear if either of them intend to have that behaviour in
this edge case, but regardless there's no reason not to just put the
data in a writable .data section.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 17:33:30 +05:30
Jessica Clarke
7f1be8a624 fw_base: Don't mark fw_platform_init as both global and weak
These are mutually exclusive. GNU as and LLVM both let later binding
directives override earlier ones so this works as intended, but LLVM 12
turned this into a warning as there's no good reason to do such a thing
and could be a potential bug. Thus, remove the redundant and incorrect
.globl directive for fw_platform_init.

Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 17:20:42 +05:30
Anup Patel
a76ac4449b lib: sbi: Fix sbi_pmu_exit() for systems not having MCOUNTINHIBIT csr
The sbi_pmu_exit() crashes on systems not having MCOUNTINHIBIT csr
so to fix this we check SBI_HART_HAS_MCOUNTINHIBIT feature in
sbi_pmu_exit() and do nothing if it is not available.

Fixes: 13d40f21d5 ("lib: sbi: Add PMU support")
Signed-off-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 14:31:21 +05:30
Bin Meng
b88b3661d4 firmware: Define a macro for version of struct fw_dynamic_info
Avoid using a magic number, instead use a macro for the version of
struct fw_dynamic_info.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 14:13:32 +05:30
Bin Meng
dcb756b01a firmware: Remove the sanity checks in fw_save_info()
The sanity checks on the magic and version was already done in
fw_boot_hart(), which happens before fw_save_info() is called.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 14:09:10 +05:30
Bin Meng
abfce9b25c docs: Make <xyz> visible in the rendered platform guide
At present in the rendered platform guide, all instances of <xyz>
are missing. Use &lt; and &gt; to replace <> to make them visible.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 11:58:31 +05:30
Bin Meng
dafaa0f54b docs: Correct a typo in platform_guide.md
It's riscv-pk, not riskv-pk.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-11 11:55:55 +05:30
Bin Meng
14c7f71c0d firmware: Minor optimization in _scratch_init()
Before entering _scratch_init(), register t3 already holds a copy
of the firmware end address, hence there is no need to calculate
it again. This reduces 3 instructions in each _scratch_init() loop.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 11:41:50 +05:30
Atish Patra
0e12aa8dee platform: generic: Add PMU support
Add PMU support for generic platform. Generic platform solely relies on
the device tree to parse all pmu related information. If any event is
not described in device tree, generic platform will not support it.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:39:42 +05:30
Atish Patra
ec1b8bb763 lib: sbi: Improve TLB function naming
Follow the standard conventon for static function names:

	All global functions should be start with sbi_<module name>_
	All static functions should be start with <module name>_

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:38:26 +05:30
Atish Patra
764a17d852 lib: sbi: Implement firmware counters
RISC-V SBI v0.3 specification defines a set of firmware events that can
provide additional information about the current firmware context. All
of the firmware event monitoring are enabled now. The firmware
events must be defined as raw perf event with MSB set as specified in the
specification.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:38:23 +05:30
Atish Patra
37f9b0f2f2 lib: sbi: Implement SBI PMU extension
RISC-V SBI specfication 0.3 defines a PMU extension that allows supervisor
mode to start/stop/configure pmu related events. This patch implements
all of the functionality defined in the specification.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:38:02 +05:30
Atish Patra
ae72ec0915 utils: fdt: Add fdt helper functions to parse PMU DT nodes
The PMU DT node bindings are defined in docs/pmu_support.md
Add few fdt helper functions to parse the DT node and update the
event-counter mapping tables.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:27:57 +05:30
Atish Patra
13d40f21d5 lib: sbi: Add PMU support
RISC-V SBI v0.3 specification defined a PMU extension to configure/start/stop
the hardware/firmware pmu events.

Implement PMU support in OpenSBI library. The implementation is agnostic of
event to counter mapping & mhpmevent value configuration. That means, it
expects platform hooks will be used to set up the mapping and provide
the mhpmevent value at runtime.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:23:18 +05:30
Atish Patra
e7cc7a3ab2 lib: sbi: Add PMU specific platform hooks
A platform hook to initialize PMU allows platform vendors to provide
their own mechanism to define pmu event-counter mappings in addition
to the DT based approach.

Another platform hook that allows platform vendors customize the
final mhpmevent value configuration.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:21:38 +05:30
Atish Patra
49966db306 lib: sbi: Use csr_read/write_num to read/update PMU counters
Currently, csr_read/write_num functions are used to read/write PMP related
CSRs where CSR value is decided at runtime. Expand this function to include
PMU related CSRs as well.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:20:44 +05:30
Atish Patra
fd9116bd46 lib: sbi: Remove redundant boot time print statement
This patch removes redundant print from sbi_boot_print_hart().

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:19:48 +05:30
Atish Patra
41ae63cd0a include: Add a list empty check function
Implement a list helper function that checks for empty lists.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:15:29 +05:30
Atish Patra
9c9b4ad24b lib: sbi: Disable m/scounteren & enable mcountinhibit
Currently, all bits in mcountern are enabled unconditionally at boot time.
With SBI PMU extension, all the programmable counters should enabled only
during performance monitoring for a particular event. However, this is done
only if mcountinhibit is implemented because the supervisor mode can not
start/stop any event without mcountinhibit.

Similarly, supervisor should take care enabling scounteren which allows
U-mode to access programmable pmu counters. All the non-programmable ones
(CY, TM, IR) should be enabled in M-mode because some userspace may rely on
builtins such as __builtin_readcyclecounter. Supervisor OS can still disable
them during initial configuration.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:14:26 +05:30
Atish Patra
0829f2bc28 lib: sbi: Detect number of bits implemented in mhpmcounter
RISC-V privilege specification allows the implementation to have less
than 64 bits.

Add a function to detect the number of implemented bits in mhpmcounter
dynamically at runtime.

Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:13:04 +05:30
Atish Patra
d3a96cc469 lib: sbi: Remove stray '\' character
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:12:06 +05:30
Atish Patra
fde28fadc2 lib: sbi: Detect mcountinihibit support at runtime
RISC-V ISA specification v1.11 defined mcountinhibit CSR that allows
software to stop any counter from incrementing. The SBI PMU extension
depends on this CSR support in hardware.

Define mcountinhibit as a hart specific feature and detect it at runtime.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:11:10 +05:30
Atish Patra
3e8b31aca9 docs: Add device tree bindings for SBI PMU extension
SBI PMU extension requires a firmware to be aware of the event to
counter/mhpmevent mappings supported by the hardware. One approach
is to encode that information in the device tree.

Define a device tree binding that allows a hardware to describe
all the PMU mappings required in concise format.

Reviewed-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Atish Patra <atish.patra@wdc.com>
2021-07-11 10:10:16 +05:30
Bin Meng
17e23b678d platform: generic: Terminate platform.name with null
fw_platform_init() fills platform.name without considering the
ending null character. Fix it.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-06 14:21:53 +05:30
Bin Meng
197e08941b docs/platform: thead-c9xx: Remove FW_PIC=y
FW_PIC is on by default. Hence no need to explicitly require it.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-06 10:50:44 +05:30
Green Wan
b7f2cd268b lib: utils: reset: unify naming of 'sifive_test' device
Unify all the file and function names of 'sifive_test' device, to use
the same prefix. This is also a preparatory patch for upcoming sifive
reset device.

Signed-off-by: Green Wan <green.wan@sifive.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-07-05 21:36:40 +05:30
Anup Patel
a731c7e369 platform: Replace CLINT library usage with ACLINT library
The ACLINT devices are backward compatible with SiFive CLINT
so we replace all CLINT library usage in various platforms
with ACLINT library. As a result of this replacement, the
CLINT library is not used by any part of OpenSBI hence we
remove it.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-24 09:39:55 +05:30
Anup Patel
03d6bb51ba lib: utils/timer: Add FDT based ACLINT MTIMER driver
We add a new FDT based ACLINT MTIMER driver which works for
both CLINT device and standalone ACLINT MTIMER device.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-24 09:39:53 +05:30
Anup Patel
56fc5f7618 lib: utils/ipi: Add FDT based ACLINT MSWI IPI driver
We add a new FDT based ACLINT MSWI IPI driver which works for both
CLINT device and standalone ACLINT MSWI device.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-24 09:39:51 +05:30
Anup Patel
bd5d2089b8 lib: utils: Add FDT parsing API common for both ACLINT and CLINT
We add fdt_parse_aclint_node() which can parse both ACLINT and
CLINT DT nodes. This means fdt_parse_clint_node() is not required
anymore so we remove it as well.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-24 09:39:48 +05:30
Anup Patel
5a049fe1d6 lib: utils/ipi: Add ACLINT MSWI library
We add common ACLINT MSWI library similar to the CLINT library
so that OpenSBI platforms can use it.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-24 09:39:44 +05:30
Anup Patel
4519e29c51 lib: utils/timer: Add ACLINT MTIMER library
We add common ACLINT MTIMER library similar to the CLINT library
so that OpenSBI platforms can use it.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-24 09:38:47 +05:30
Heinrich Schuchardt
11c345f14a lib: simplify sbi_fifo_inplace_update()
Don't assign an unused value to variable index.

Use operator '-=' where applicable.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-22 19:16:52 +05:30
Heinrich Schuchardt
99017946f3 lib: sign conflict in wake_coldboot_harts()
Compiling wake_coldboot_harts() with GCC 11 and -Wextra yields:

lib/sbi/sbi_init.c:208:27:
error: comparison of integer expressions of different signedness:
‘int’ and ‘u32’ {aka ‘unsigned int’} [-Werror=sign-compare]
  208 |         for (int i = 0; i <= sbi_scratch_last_hartid(); i++) {
      |                           ^~

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-22 19:14:30 +05:30
Heinrich Schuchardt
cdcf907b19 lib: sign conflict in sbi_tlb_entry_process()
Compiling sbi_tlb_entry_process() with GCC 11 and -Wextra yields:

lib/sbi/sbi_tlb.c: In function ‘sbi_tlb_process_count’:
lib/sbi/sbi_tlb.c:206:31:
error: comparison of integer expressions of different signedness:
‘u32’ {aka ‘unsigned int’} and ‘int’ [-Werror=sign-compare]
  206 |                 if (deq_count > count)

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-06-22 19:12:22 +05:30
Bin Meng
eb90e0a16c lib: utils/libfdt: Upgrade to v1.6.1 release
Sync with libfdt v1.6.1 release source codes.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-22 17:42:29 +05:30
Charles Papon
79f9b4220f lib: sbi: Fix GET_F64_REG inline assembly
Current, GET_F64_REG() macro does not generate correct inline
assembly for the RV32 systems. This patch provides separate
definitions of GET_F64_REG() macro for RV32 and RV64 systems.

Signed-off-by: Charles Papon <charles.papon.90@gmail.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
2021-06-12 09:53:33 +05:30
Heinrich Schuchardt
360ab88569 lib: utils: missing initialization in thead_reset_init
If property csr-copy does not exist, fdt_getprop() will return NULL and cnt
will have a random value from the stack.

Call clone_csrs() only if cnt is initialized to a non-zero value.

Fixes: 49e422c5ad ("lib: utils: reset: Add T-HEAD sample platform reset driver")
Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-11 19:20:16 +05:30
Heinrich Schuchardt
1da3d80b5b lib: sbi_scratch: zero out scratch memory on all harts
In sbi_scratch_init() we determine the last hart. The index of the last
hart cannot exceed SBI_HARTMASK_MAX_BITS - 1. We should not initialize
last_hartid_having_scratch to a higher number to avoid buffer overflows
when using this value before calling sbi_scratch_init().

When allocating scratch memory in sbi_scratch_alloc_offset() we zero out
the allocated memory for all harts except for the last one. We should not
skip the last hart.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-11 13:59:48 +05:30
Bin Meng
434198e3be platform: andes/ae350: Drop plicsw_ipi_sync()
plicsw_ipi_sync() is a forward declaration but without the actual
implementation. Drop it.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-11 11:21:14 +05:30
Bin Meng
de446ccf18 platform: andes/ae350: Drop plicsw_get_pending()
This is not used anywhere. Drop it.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-11 11:20:18 +05:30
Bin Meng
b32fac4b65 docs/platform: andes-ae350: Fix missing spaces
Fix several places in the docmentation that are missing spaces.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-11 11:18:07 +05:30
Bin Meng
a03ea2e2b1 platform: andes/ae350: Cosmetic fixes in plicsw.c
- %s/CLINT/PLICSW
- replace '.' with a space
- add a space around * in plicsw_cold_ipi_init()

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-11 11:16:46 +05:30
Heinrich Schuchardt
f30b18944e lib: sbi_scratch: remove owner from sbi_scratch_alloc_offset
The parameter owner of function sbi_scratch_alloc_offset() is never used.
The scratch memory is small. We should not use it for debug information in
future. Hence eliminate the parameter.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-06-02 17:07:26 +05:30
Heinrich Schuchardt
66c4fca532 lib: utils: consider ':' in stdout-path
The value of the /chosen/stdout-path devicetree property is used to
determine the UART used by openSBI. According to the devicetree
specification the value may contain a hyphen, e.g.

	chosen {
                stdout-path = "/serial@f00:115200";
        };

If the character ':' is present, it terminates the path of the device.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-06-02 17:06:17 +05:30
Heinrich Schuchardt
d9ba6536d3 docs: debugging OpenSBI
Describe how to debug OpenSBI on QEMU with GDB.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
2021-06-02 17:04:07 +05:30
Anup Patel
54d7def6c2 lib: utils: Try other FDT drivers when we see SBI_ENODEV
We should try other FDT drivers when we see SBI_ENODEV returned
by cold_init() of FDT driver.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-05-24 15:49:21 +05:30
Anup Patel
b2dbbc0577 lib: Check region base for merging in sbi_domain_root_add_memregion()
We can merge region B onto region A only if base of region A is
aligned to region A order + 1.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-05-24 15:47:41 +05:30
Daniel Schaefer
fe92347b9f lib: utils/fdt: Replace strcmp with strncmp
Use strncmp() instead of strcmp() in __fixup_find_domain_offset()
so that it compiles fine when linking with external firmware (such
as EDK2).

Signed-off-by: Daniel Schaefer <daniel.schaefer@hpe.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Abner Chang <abner.chang@hpe.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-19 17:35:38 +05:30
Daniel Schaefer
ee7c2b27ea lib: utils/fdt: Don't use sbi_string functions
When SBI is built by external firmware, we need to use their functions,
defined in libfdt_env.h.

Just like 2cfd2fc904

Signed-off-by: Daniel Schaefer <daniel.schaefer@hpe.com>
Reviewed-by: Abner Chang <abner.chang@hpe.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-19 17:34:06 +05:30
Daniel Schaefer
c9ef2bc7e4 lib: utils: Add strncpy macro to libfdt_env.h
we want to use sbi_strncpy as strncpy in the OpenSBI implementation for
libfdt.

Just like 2845d2d2cf

Signed-off-by: Daniel Schaefer <daniel.schaefer@hpe.com>
Reviewed-by: Abner Chang <abner.chang@hpe.com>
Reviewed-by: Xiang W <wxjstz@126.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-19 17:33:33 +05:30
Fabrice Fontaine
6139ab272b Makefile: unconditionally disable SSP
Though -nostdlib is passed in CFLAGS, -fno-stack-protector must also be
passed to avoid linking errors related to undefined references to
'__stack_chk_guard' and '__stack_chk_fail' if toolchain enforces
-fstack-protector.

Fixes:
 - https://gitlab.com/kubu93/buildroot/-/jobs/1247043359

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Xiang W <wxjstz@126.com>
2021-05-19 16:37:50 +05:30
Bin Meng
e822b7504d lib: utils/serial: Support Synopsys DesignWare APB UART
Synopsys DesignWare APB UART is seen on the StarFive JH7100 SoC.
Its programming interface is compatible with the existing 8250
UART driver. Simply add its compatible string to the driver makes
it work with the StarFive JH7100 SoC on a BeagleV board.

With this patch, the generic platform firmware can be used out of
the box on the BeagleV board.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-19 13:03:19 +05:30
Daniel Schaefer
f90c4c2e02 lib: sbi: Have spinlock checks return bool
spin_lock_check already returned bool in the source file but not in the
header. With some toolchains that causes an error, as it should.

Because it and related functions all essentially return a bool, we can
use this opportunity to change them.

Signed-off-by: Daniel Schaefer <git@danielschaefer.me>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-14 17:05:20 +05:30
Bin Meng
26998f3d11 platform: Remove sifive/fu540 platform
Now that SiFive HiFive Unleashed board is using the generic platform
support in OpenSBI, let's remove the old non-generic one.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-06 14:24:45 +05:30
Bin Meng
d4177e7217 docs: platform: Describe sifive_fu540 as supported generic platform
The upstream U-Boot/QEMU have been using generic platform for SiFive
HiFive Unleashed board for some time. Let's document sifive_fu540 as
one of the supported targets for "generic" platform.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-06 14:23:38 +05:30
Bin Meng
552f53f360 docs: platform: Sort platform names
Let's sort the platform names in alphabetical order.

Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
2021-05-06 14:22:34 +05:30
Daniel Cederman
117fb6dcb1 lib: utils/serial: Add support for Gaisler APBUART
This patch adds support for the UART used by the NOEL-V processor.

Cobham Gaisler's NOEL-V RISC-V processor IP is available under GPL
and commercial license and is described in more detail at
https://www.gaisler.com/noelv.

Signed-off-by: Daniel Cederman <cederman@gaisler.com>
Reviewed-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
2021-05-06 14:21:07 +05:30
152 changed files with 6091 additions and 1141 deletions

122
Makefile
View File

@@ -76,26 +76,54 @@ OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sb
OPENSBI_VERSION_GIT=$(shell if [ -d $(src_dir)/.git ]; then git describe 2> /dev/null; fi)
# Setup compilation commands
ifneq ($(LLVM),)
CC = clang
AR = llvm-ar
LD = ld.lld
OBJCOPY = llvm-objcopy
else
ifdef CROSS_COMPILE
CC = $(CROSS_COMPILE)gcc
CPP = $(CROSS_COMPILE)cpp
AR = $(CROSS_COMPILE)ar
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
else
CC ?= gcc
CPP ?= cpp
AR ?= ar
LD ?= ld
OBJCOPY ?= objcopy
endif
endif
CPP = $(CC) -E
AS = $(CC)
DTC = dtc
# Guess the compillers xlen
OPENSBI_CC_XLEN := $(shell TMP=`$(CC) -dumpmachine | sed 's/riscv\([0-9][0-9]\).*/\1/'`; echo $${TMP})
ifneq ($(shell $(CC) --version 2>&1 | head -n 1 | grep clang),)
CC_IS_CLANG = y
else
CC_IS_CLANG = n
endif
ifneq ($(shell $(LD) --version 2>&1 | head -n 1 | grep LLD),)
LD_IS_LLD = y
else
LD_IS_LLD = n
endif
ifeq ($(CC_IS_CLANG),y)
ifneq ($(CROSS_COMPILE),)
CLANG_TARGET = --target=$(notdir $(CROSS_COMPILE:%-=%))
endif
endif
# Guess the compiler's XLEN
OPENSBI_CC_XLEN := $(shell TMP=`$(CC) $(CLANG_TARGET) -dumpmachine | sed 's/riscv\([0-9][0-9]\).*/\1/'`; echo $${TMP})
# Guess the compiler's ABI and ISA
ifneq ($(CC_IS_CLANG),y)
OPENSBI_CC_ABI := $(shell TMP=`$(CC) -v 2>&1 | sed -n 's/.*\(with\-abi=\([a-zA-Z0-9]*\)\).*/\2/p'`; echo $${TMP})
OPENSBI_CC_ISA := $(shell TMP=`$(CC) -v 2>&1 | sed -n 's/.*\(with\-arch=\([a-zA-Z0-9]*\)\).*/\2/p'`; echo $${TMP})
endif
# Setup platform XLEN
ifndef PLATFORM_RISCV_XLEN
@@ -106,6 +134,44 @@ ifndef PLATFORM_RISCV_XLEN
endif
endif
ifeq ($(CC_IS_CLANG),y)
ifeq ($(CROSS_COMPILE),)
CLANG_TARGET = --target=riscv$(PLATFORM_RISCV_XLEN)-unknown-elf
endif
endif
ifeq ($(LD_IS_LLD),y)
RELAX_FLAG = -mno-relax
USE_LD_FLAG = -fuse-ld=lld
else
USE_LD_FLAG = -fuse-ld=bfd
endif
# Check whether the linker supports creating PIEs
OPENSBI_LD_PIE := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) $(USE_LD_FLAG) -fPIE -nostdlib -Wl,-pie -x c /dev/null -o /dev/null >/dev/null 2>&1 && echo y || echo n)
# Check whether the compiler supports -m(no-)save-restore
CC_SUPPORT_SAVE_RESTORE := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) -nostdlib -mno-save-restore -x c /dev/null -o /dev/null 2>&1 | grep "\-save\-restore" >/dev/null && echo n || echo y)
# Build Info:
# OPENSBI_BUILD_TIME_STAMP -- the compilation time stamp
# OPENSBI_BUILD_COMPILER_VERSION -- the compiler version info
BUILD_INFO ?= n
ifeq ($(BUILD_INFO),y)
OPENSBI_BUILD_DATE_FMT = +%Y-%m-%d %H:%M:%S %z
ifdef SOURCE_DATE_EPOCH
OPENSBI_BUILD_TIME_STAMP ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" \
"$(OPENSBI_BUILD_DATE_FMT)" 2>/dev/null || \
date -u -r "$(SOURCE_DATE_EPOCH)" \
"$(OPENSBI_BUILD_DATE_FMT)" 2>/dev/null || \
date -u "$(OPENSBI_BUILD_DATE_FMT)")
else
OPENSBI_BUILD_TIME_STAMP ?= $(shell date "$(OPENSBI_BUILD_DATE_FMT)")
endif
OPENSBI_BUILD_COMPILER_VERSION=$(shell $(CC) -v 2>&1 | grep ' version ' | \
sed 's/[[:space:]]*$$//')
endif
# Setup list of objects.mk files
ifdef PLATFORM
platform-object-mks=$(shell if [ -d $(platform_src_dir)/ ]; then find $(platform_src_dir) -iname "objects.mk" | sort -r; fi)
@@ -194,20 +260,32 @@ else
endif
# Setup compilation commands flags
GENFLAGS = -I$(platform_src_dir)/include
ifeq ($(CC_IS_CLANG),y)
GENFLAGS += $(CLANG_TARGET)
GENFLAGS += -Wno-unused-command-line-argument
endif
GENFLAGS += -I$(platform_src_dir)/include
GENFLAGS += -I$(include_dir)
ifneq ($(OPENSBI_VERSION_GIT),)
GENFLAGS += -DOPENSBI_VERSION_GIT="\"$(OPENSBI_VERSION_GIT)\""
endif
ifeq ($(BUILD_INFO),y)
GENFLAGS += -DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
GENFLAGS += -DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
endif
GENFLAGS += $(libsbiutils-genflags-y)
GENFLAGS += $(platform-genflags-y)
GENFLAGS += $(firmware-genflags-y)
CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-strict-aliasing -O2
CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
CFLAGS += -mno-save-restore -mstrict-align
CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-stack-protector -fno-strict-aliasing -O2
CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align
# enable -m(no-)save-restore option by CC_SUPPORT_SAVE_RESTORE
ifeq ($(CC_SUPPORT_SAVE_RESTORE),y)
CFLAGS += -mno-save-restore
endif
CFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)
CFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
CFLAGS += $(RELAX_FLAG)
CFLAGS += $(GENFLAGS)
CFLAGS += $(platform-cflags-y)
CFLAGS += -fno-pie -no-pie
@@ -218,22 +296,36 @@ CPPFLAGS += $(platform-cppflags-y)
CPPFLAGS += $(firmware-cppflags-y)
ASFLAGS = -g -Wall -nostdlib
ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
ASFLAGS += -mno-save-restore -mstrict-align
ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align
# enable -m(no-)save-restore option by CC_SUPPORT_SAVE_RESTORE
ifeq ($(CC_SUPPORT_SAVE_RESTORE),y)
ASFLAGS += -mno-save-restore
endif
ASFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)
ASFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
ASFLAGS += $(RELAX_FLAG)
ifneq ($(CC_IS_CLANG),y)
ifneq ($(RELAX_FLAG),)
ASFLAGS += -Wa,$(RELAX_FLAG)
endif
endif
ASFLAGS += $(GENFLAGS)
ASFLAGS += $(platform-asflags-y)
ASFLAGS += $(firmware-asflags-y)
ARFLAGS = rcs
ELFFLAGS += -Wl,--build-id=none -N -static-libgcc -lgcc
ELFFLAGS += $(USE_LD_FLAG)
ELFFLAGS += -Wl,--build-id=none -Wl,-N
ELFFLAGS += $(platform-ldflags-y)
ELFFLAGS += $(firmware-ldflags-y)
MERGEFLAGS += -r
ifeq ($(LD_IS_LLD),y)
MERGEFLAGS += -b elf
else
MERGEFLAGS += -b elf$(PLATFORM_RISCV_XLEN)-littleriscv
endif
MERGEFLAGS += -m elf$(PLATFORM_RISCV_XLEN)lriscv
DTSCPPFLAGS = $(CPPFLAGS) -nostdinc -nostdlib -fno-builtin -D__DTS__ -x assembler-with-cpp
@@ -338,6 +430,11 @@ $(build_dir)/%.dep: $(src_dir)/%.c
$(build_dir)/%.o: $(src_dir)/%.c
$(call compile_cc,$@,$<)
ifeq ($(BUILD_INFO),y)
$(build_dir)/lib/sbi/sbi_init.o: $(libsbi_dir)/sbi_init.c FORCE
$(call compile_cc,$@,$<)
endif
$(build_dir)/%.dep: $(src_dir)/%.S
$(call compile_as_dep,$@,$<)
@@ -491,3 +588,6 @@ ifeq ($(install_root_dir),$(install_root_dir_default)/usr)
$(if $(V), @echo " RM $(install_root_dir_default)")
$(CMD_PREFIX)rm -rf $(install_root_dir_default)
endif
.PHONY: FORCE
FORCE:

View File

@@ -96,8 +96,21 @@ Required Toolchain
------------------
OpenSBI can be compiled natively or cross-compiled on a x86 host. For
cross-compilation, you can build your own toolchain or just download
a prebuilt one from the [Bootlin toolchain repository].
cross-compilation, you can build your own toolchain, download a prebuilt one
from the [Bootlin toolchain repository] or install a distribution-provided
toolchain; if you opt to use LLVM/Clang, most distribution toolchains will
support cross-compiling for RISC-V using the same toolchain as your native
LLVM/Clang toolchain due to LLVM's ability to support multiple backends in the
same binary, so is often an easy way to obtain a working cross-compilation
toolchain.
Basically, we prefer toolchains with Position Independent Executable (PIE)
support like *riscv64-linux-gnu-gcc*, *riscv64-unknown-freebsd-gcc*, or
*Clang/LLVM* as they generate PIE firmware images that can run at arbitrary
address with appropriate alignment. If a bare-metal GNU toolchain (e.g.
*riscv64-unknown-elf-gcc*) is used, static linked firmware images are
generated instead. *Clang/LLVM* can still generate PIE images if a bare-metal
triple is used (e.g. *-target riscv64-unknown-elf*).
Please note that only a 64-bit version of the toolchain is available in
the Bootlin toolchain repository for now.
@@ -111,7 +124,7 @@ architecture than RISC-V.
For cross-compiling, the environment variable *CROSS_COMPILE* must be defined
to specify the name prefix of the RISC-V compiler toolchain executables, e.g.
*riscv64-unknown-elf-* if the gcc executable used is *riscv64-unknown-elf-gcc*.
*riscv64-linux-gnu-* if the gcc executable used is *riscv64-linux-gnu-gcc*.
To build *libsbi.a* simply execute:
```
@@ -187,21 +200,83 @@ Building 32-bit / 64-bit OpenSBI Images
---------------------------------------
By default, building OpenSBI generates 32-bit or 64-bit images based on the
supplied RISC-V cross-compile toolchain. For example if *CROSS_COMPILE* is set
to *riscv64-unknown-elf-*, 64-bit OpenSBI images will be generated. If building
to *riscv64-linux-gnu-*, 64-bit OpenSBI images will be generated. If building
32-bit OpenSBI images, *CROSS_COMPILE* should be set to a toolchain that is
pre-configured to generate 32-bit RISC-V codes, like *riscv32-unknown-elf-*.
pre-configured to generate 32-bit RISC-V codes, like *riscv32-linux-gnu-*.
However it's possible to explicitly specify the image bits we want to build with
a given RISC-V toolchain. This can be done by setting the environment variable
*PLATFORM_RISCV_XLEN* to the desired width, for example:
```
export CROSS_COMPILE=riscv64-unknown-elf-
export CROSS_COMPILE=riscv64-linux-gnu-
export PLATFORM_RISCV_XLEN=32
```
will generate 32-bit OpenSBI images. And vice vesa.
Building with Clang/LLVM
------------------------
OpenSBI can also be built with Clang/LLVM. To build with just Clang but keep
the default binutils (which will still use the *CROSS_COMPILE* prefix if
defined), override the *CC* make variable with:
```
make CC=clang
```
To build with a full LLVM-based toolchain, not just Clang, enable the *LLVM*
option with:
```
make LLVM=1
```
When using Clang, *CROSS_COMPILE* often does not need to be defined unless
using GNU binutils with prefixed binary names. *PLATFORM_RISCV_XLEN* will be
used to infer a default triple to pass to Clang, so if *PLATFORM_RISCV_XLEN*
itself defaults to an undesired value then prefer setting that rather than the
full triple via *CROSS_COMPILE*. If *CROSS_COMPILE* is nonetheless defined,
rather than being used as a prefix for the executable name, it will instead be
passed via the `--target` option with the trailing `-` removed, so must be a
valid triple.
These can also be mixed; for example using a GCC cross-compiler but LLVM
binutils would be:
```
make CC=riscv64-linux-gnu-gcc LLVM=1
```
These variables must be passed for all the make invocations described in this
document.
NOTE: Using Clang with a `riscv*-linux-gnu` GNU binutils linker has been seen
to produce broken binaries with missing relocations; it is therefore currently
recommended that this combination be avoided or *FW_PIC=n* be used to disable
building OpenSBI as a position-independent binary.
Building with timestamp and compiler info
-----------------------------------------
When doing development, we may want to know the build time and compiler info
for debug purpose. OpenSBI can also be built with timestamp and compiler info.
To build with those info and print it out at boot time, we can just simply add
`BUILD_INFO=y`, like:
```
make BUILD_INFO=y
```
But if you have used `BUILD_INFO=y`, and want to switch back to `BUILD_INFO=n`,
you must do
```
make clean
```
before the next build.
NOTE: Using `BUILD_INFO=y` without specifying SOURCE_DATE_EPOCH will violate
[reproducible builds]. This definition is ONLY for development and debug
purpose, and should NOT be used in a product which follows "reproducible
builds".
Contributing to OpenSBI
-----------------------
@@ -284,3 +359,4 @@ make I=<install_directory> install_docs
[Doxygen manual]: http://www.doxygen.nl/manual/index.html
[Kendryte standalone SDK]: https://github.com/kendryte/kendryte-standalone-sdk
[third party notices]: ThirdPartyNotices.md
[reproducible builds]: https://reproducible-builds.org

View File

@@ -9,6 +9,13 @@ OpenSBI generic library code. The supported firmwares type will differ in how
the arguments passed by the platform early boot stage are handled, as well as
how the boot stage following the firmware will be handled and executed.
The previous booting stage will pass information via the following registers
of RISC-V CPU:
* hartid via *a0* register
* device tree blob address in memory via *a1* register. The address must
be aligned to 8 bytes.
OpenSBI currently supports three different types of firmwares.
Firmware with Dynamic Information (*FW_DYNAMIC*)
@@ -62,6 +69,12 @@ parameters:
argument by the prior booting stage.
* **FW_FDT_PADDING** - Optional zero bytes padding to the embedded flattened
device tree binary file specified by **FW_FDT_PATH** option.
* **FW_PIC** - "FW_PIC=y" generates position independent executable firmware
images. OpenSBI can run at arbitrary address with appropriate alignment.
Therefore, the original relocation mechanism ("FW_PIC=n") will be skipped.
In other words, OpenSBI will directly run at the load address without any
code movement. This option requires a toolchain with PIE support, and it
is on by default.
Additionally, each firmware type as a set of type specific configuration
parameters. Detailed information for each firmware type can be found in the

View File

@@ -6,8 +6,9 @@ information about next booting stage (e.g. a bootloader or an OS) and runtime
OpenSBI library options from previous booting stage.
The previous booting stage will pass information to *FW_DYNAMIC* by creating
*struct fw_dynamic_info* in memory and passing it's address to *FW_DYNAMIC*
via *a2* register of RISC-V CPU.
*struct fw_dynamic_info* in memory and passing its address to *FW_DYNAMIC*
via *a2* register of RISC-V CPU. The address must be aligned to 8 bytes on
RV64 and 4 bytes on RV32.
A *FW_DYNAMIC* firmware is particularly useful when the booting stage executed
prior to OpenSBI firmware is capable of loading both the OpenSBI firmware and

View File

@@ -1,9 +1,9 @@
Andes AE350 SoC Platform
========================
The AE350 AXI/AHB-based platform N25(F)/NX25(F)/D25F/A25/AX25 CPU with level-one
memories,interrupt controller, debug module, AXI and AHB Bus Matrix Controller,
AXI-to-AHB Bridge and a collection of fundamentalAHB/APB bus IP components
pre-integrated together as a system design.The high-quality and configurable
memories, interrupt controller, debug module, AXI and AHB Bus Matrix Controller,
AXI-to-AHB Bridge and a collection of fundamental AHB/APB bus IP components
pre-integrated together as a system design. The high-quality and configurable
AHB/APB IPs suites a majority embedded systems, and the verified platform serves
as a starting point to jump start SoC designs.

View File

@@ -46,11 +46,13 @@ RISC-V Platforms Using Generic Platform
---------------------------------------
* **QEMU RISC-V Virt Machine** (*[qemu_virt.md]*)
* **Spike** (*[spike.md]*)
* **Shakti C-class SoC Platform** (*[shakti_cclass.md]*)
* **SiFive HiFive Unleashed** (*[sifive_fu540.md]*)
* **Spike** (*[spike.md]*)
* **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*)
[qemu_virt.md]: qemu_virt.md
[spike.md]: spike.md
[shakti_cclass.md]: shakti_cclass.md
[sifive_fu540.md]: sifive_fu540.md
[spike.md]: spike.md
[thead-c9xx.md]: thead-c9xx.md

View File

@@ -147,3 +147,27 @@ qemu-system-riscv32 -M virt -m 256M -nographic \
-device virtio-blk-device,drive=hd0 \
-append "root=/dev/vda rw console=ttyS0"
```
Debugging with GDB
------------------
In a first console start OpenSBI with QEMU:
```
qemu-system-riscv64 -M virt -m 256M -nographic \
-bios build/platform/generic/firmware/fw_payload.bin \
-gdb tcp::1234 \
-S
```
Parameter *-gdb tcp::1234* specifies 1234 as the debug port.
Parameter *-S* lets QEMU wait at the first instruction.
In a second console start GDB:
```
gdb build/platform/generic/firmware/fw_payload.elf \
-ex 'target remote localhost:1234'
```

View File

@@ -8,7 +8,7 @@ With QEMU v4.2 or above release, the 'sifive_u' machine can be used to test
OpenSBI image built for the real hardware as well.
To build platform specific library and firmwares, provide the
*PLATFORM=sifive/fu540* parameter to the top level `make` command.
*PLATFORM=generic* parameter to the top level `make` command.
Platform Options
----------------
@@ -32,10 +32,10 @@ To use Linux v5.2 (or higher), the pre-built DTB (DT binary) from Linux v5.2
the compile time option *FW_FDT_PATH*.
```
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image
or
(For Linux v5.2 or higher)
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel>
make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Image FW_FDT_PATH=<hifive-unleashed-a00.dtb path from Linux kernel>
```
**U-Boot Payload**
@@ -45,7 +45,7 @@ sifive_fu540_defconfig configuration and with U-Boot v2020.01, and up to
v2021.04. sifive_unleashed_defconfig shall be used with v2021.07 or above.
```
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot-dtb.bin
make PLATFORM=generic FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot-dtb.bin
```
For U-Boot v2020.07-rc4 or later releases, SPL support was added in U-Boot.
Please refer to the detailed U-Boot booting guide available at [U-Boot].
@@ -64,7 +64,7 @@ That's why the generated firmware binary in above steps should be copied to
the partition of the sdcard with above GUID.
```
dd if=build/platform/sifive/fu540/firmware/fw_payload.bin of=/dev/disk2s1 bs=1024
dd if=build/platform/generic/firmware/fw_payload.bin of=/dev/disk2s1 bs=1024
```
In my case, it is the first partition is **disk2s1** that has been formatted
@@ -161,7 +161,7 @@ U-Boot uses the DTB generated by QEMU, and u-boot.bin should be used as the
payload image, like:
```
make PLATFORM=sifive/fu540 FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin
make PLATFORM=generic FW_PAYLOAD_PATH=<u-boot_build_dir>/u-boot.bin
```
U-Boot v2020.07 release added SPL support to SiFive HiFive Unleashed board,
@@ -179,12 +179,12 @@ The above errors can be safely ignored as we don't run U-Boot SPL under QEMU.
Run:
```
qemu-system-riscv64 -M sifive_u -m 256M -nographic \
-bios build/platform/sifive/fu540/firmware/fw_payload.bin
-bios build/platform/generic/firmware/fw_payload.bin
```
or
```
qemu-system-riscv64 -M sifive_u -m 256M -nographic \
-bios build/platform/sifive/fu540/firmware/fw_jump.bin \
-bios build/platform/generic/firmware/fw_jump.bin \
-kernel <uboot_build_dir>/u-boot.bin
```

View File

@@ -43,7 +43,18 @@ make PLATFORM=generic FW_PAYLOAD_PATH=<linux_build_directory>/arch/riscv/boot/Im
Run:
```
spike --initrd <path_to_cpio_ramdisk> build/platform/generic/firmware/fw_payload.elf
spike -m256 \
--initrd <path_to_cpio_ramdisk> \
--bootargs 'root=/dev/ram rw console=hvc0 earlycon=sbi' \
build/platform/generic/firmware/fw_payload.elf
```
or
```
spike -m256 \
--kernel <linux_build_directory>/arch/riscv/boot/Image \
--initrd <path_to_cpio_ramdisk> \
--bootargs 'root=/dev/ram rw console=hvc0 earlycon=sbi' \
build/platform/generic/firmware/fw_jump.elf
```
Execution on QEMU RISC-V 64-bit

View File

@@ -16,7 +16,7 @@ The *T-HEAD C9xx* does not have any platform-specific compile options
because it use generic platform.
```
CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic FW_PIC=y /usr/bin/make
CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic /usr/bin/make
```
The *T-HEAD C9xx* DTB provided to OpenSBI generic firmwares will usually have
@@ -51,11 +51,6 @@ DTS Example1: (Single core, eg: Allwinner D1 - c906)
compatible = "simple-bus";
ranges;
reset: reset-sample {
compatible = "thead,reset-sample";
plic-delegate = <0x0 0x101ffffc>;
};
clint0: clint@14000000 {
compatible = "riscv,clint0";
interrupts-extended = <
@@ -67,7 +62,8 @@ DTS Example1: (Single core, eg: Allwinner D1 - c906)
intc: interrupt-controller@10000000 {
#interrupt-cells = <1>;
compatible = "riscv,plic0";
compatible = "allwinner,sun20i-d1-plic",
"thead,c900-plic";
interrupt-controller;
interrupts-extended = <
&cpu0_intc 0xffffffff &cpu0_intc 9
@@ -150,7 +146,6 @@ DTS Example2: (Multi cores with soc reset-regs)
reset: reset-sample {
compatible = "thead,reset-sample";
plic-delegate = <0xff 0xd81ffffc>;
entry-reg = <0xff 0xff019050>;
entry-cnt = <4>;
control-reg = <0xff 0xff015004>;
@@ -173,7 +168,7 @@ DTS Example2: (Multi cores with soc reset-regs)
intc: interrupt-controller@ffd8000000 {
#interrupt-cells = <1>;
compatible = "riscv,plic0";
compatible = "thead,c900-plic";
interrupt-controller;
interrupts-extended = <
&cpu0_intc 0xffffffff &cpu0_intc 9
@@ -194,7 +189,6 @@ DTS Example2: (Multi cores with old reset csrs)
```
reset: reset-sample {
compatible = "thead,reset-sample";
plic-delegate = <0xff 0xd81ffffc>;
using-csr-reset;
csr-copy = <0x7c0 0x7c1 0x7c2 0x7c3 0x7c5 0x7cc
0x3b0 0x3b1 0x3b2 0x3b3

View File

@@ -17,7 +17,7 @@ the supported platforms. These firmwares are linked against *libplatsbi.a*.
Firmware binaries are installed in
*<install_directory>/platform/<platform_subdir>/bin*. These firmwares can be
used as executable runtime firmwares on the supported platforms as a replacement
for the legacy *riskv-pk* boot loader (BBL).
for the legacy *riscv-pk* boot loader (BBL).
A complete doxygen-style documentation of *struct sbi_platform* and related
APIs is available in the file *include/sbi/sbi_platform.h*.
@@ -25,18 +25,18 @@ APIs is available in the file *include/sbi/sbi_platform.h*.
Adding support for a new platform
---------------------------------
Support for a new platform named *<xyz>* can be added as follows:
Support for a new platform named *&lt;xyz&gt;* can be added as follows:
1. Create a directory named *<xyz>* under the *platform/* directory.
1. Create a directory named *&lt;xyz&gt;* under the *platform/* directory.
2. Create a platform configuration file named *config.mk* under the
*platform/<xyz>/* directory. This configuration file will provide
*platform/&lt;xyz&gt;/* directory. This configuration file will provide
compiler flags, and select firmware options.
3. Create a *platform/<xyz>/objects.mk* file for listing the
3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the
platform-specific object files to be compiled.
4. Create a *platform/<xyz>/platform.c* file providing a *struct sbi_platform*
instance.
4. Create a *platform/&lt;xyz&gt;/platform.c* file providing a
*struct sbi_platform* instance.
A platform support code template is available under the *platform/template*
directory. Copying this directory and its content as a new directory named
*<xyz>* under the *platform/* directory will create all the files mentioned
above.
*&lt;xyz&gt;* under the *platform/* directory will create all the files
mentioned above.

87
docs/pmu_support.md Normal file
View File

@@ -0,0 +1,87 @@
OpenSBI SBI PMU extension support
==================================
SBI PMU extension supports allow supervisor software to configure/start/stop
any performance counter at anytime. Thus, an user can leverage full
capability of performance analysis tools such as perf if SBI PMU extension is
enabled. The OpenSBI implementation makes the following assumptions about the
hardware platform.
* MCOUNTINHIBIT CSR must be implemented in the hardware. Otherwise, SBI PMU
extension will not be enabled.
* The platform must provide information about PMU event to counter mapping
via device tree or platform specific hooks. Otherwise, SBI PMU extension will
not be enabled.
* The platforms should provide information about the PMU event selector values
that should be encoded in the expected value of MHPMEVENTx while configuring
MHPMCOUNTERx for that specific event. This can be done via a device tree or
platform specific hooks. The exact value to be written to he MHPMEVENTx is
completely depends on platform. Generic platform writes the zero-extended event_idx
as the expected value for hardware cache/generic events as suggested by the SBI
specification.
SBI PMU Device Tree Bindings
----------------------------
Platforms may choose to describe PMU event selector and event to counter mapping
values via device tree. The following sections describes the PMU DT node
bindings in details.
* **compatible** (Mandatory) - The compatible string of SBI PMU device tree node.
This DT property must have the value **riscv,pmu**.
* **riscv,event-to-mhpmevent**(Optional) - It represents an ONE-to-ONE mapping
between a PMU event and the event selector value that platform expects to be
written to the MHPMEVENTx CSR for that event. The mapping is encoded in a
table format where each row represents an event. The first column represent the
event idx where the 2nd & 3rd column represent the event selector value that
should be encoded in the expected value to be written in MHPMEVENTx.
This property shouldn't encode any raw hardware event.
* **riscv,event-to-mhpmcounters**(Optional) - It represents a MANY-to-MANY
mapping between a range of events and all the MHPMCOUNTERx in a bitmap format
that can be used to monitor these range of events. The information is encoded in
a table format where each row represent a certain range of events and
corresponding counters. The first column represents starting of the pmu event id
and 2nd column represents the end of the pmu event id. The third column
represent a bitmap of all the MHPMCOUNTERx. This property is mandatory if
event-to-mhpmevent is present. Otherwise, it can be omitted. This property
shouldn't encode any raw event.
* **riscv,raw-event-to-mhpmcounters**(Optional) - It represents an ONE-to-MANY
or MANY-to-MANY mapping between the raw event(s) and all the MHPMCOUNTERx in
a bitmap format that can be used to monitor that raw event, which depends on
how the platform encodes the monitor events. Currently, only the following three
encoding methods are supported, encoding each event as a number, using a bitmap
to encode monitor events, and mixing the previous two methods. The information
is encoded in a table format where each row represent the specific raw event(s).
The first column represents a 64-bit selector value which can indicate an
monitor event ID (encoded by a number) or an event set (encoded by a bitmap).
In case of the latter, the lower bits used to encode a set of events should be
set to zero. The second column is a 64-bit selector mask where any bits used
for event encoding will be cleared. If a platform directly encodes each raw PMU
event as a unique ID, the value of select_mask will be 0xffffffff_ffffffff.
The third column represent a bitmap of all the MHPMCOUNTERx that can be used for
monitoring the specified event(s).
*Note:* A platform may choose to provide the mapping between event & counters
via platform hooks rather than the device tree.
### Example
```
pmu {
compatible = "riscv,pmu";
interrupts = <0x100>;
interrupt-parent = <&plic>
riscv,event-to-mhpmevent = <0x0000B 0x0000 0x0001>,
riscv,event-to-mhpmcounters = <0x00001 0x00001 0x00000001>,
<0x00002 0x00002 0x00000004>,
<0x00003 0x0000A 0x00000ff8>,
<0x10000 0x10033 0x000ff000>,
riscv,raw-event-to-mhpmcounters = <0x0000 0x0002 0xffffffff 0xffffffff 0x00000f8>,
<0xffffffff 0xfffffff0 0xffffffff 0xfffffff0 0x00000ff0>,
};
```

View File

@@ -65,14 +65,14 @@ _try_lottery:
/* Save load address */
lla t0, _load_start
lla t1, _start
lla t1, _fw_start
REG_S t1, 0(t0)
#ifdef FW_PIC
/* relocate the global table content */
lla t0, _link_start
REG_L t0, 0(t0)
/* t1 shall has the address of _start */
/* t1 shall has the address of _fw_start */
sub t2, t1, t0
lla t3, _runtime_offset
REG_S t2, (t3)
@@ -172,7 +172,7 @@ _relocate_copy_to_upper_loop:
blt t0, t1, _relocate_copy_to_upper_loop
jr t4
_wait_relocate_copy_done:
lla t0, _start
lla t0, _fw_start
lla t1, _link_start
REG_L t1, 0(t1)
beq t0, t1, _wait_for_boot_hart
@@ -278,6 +278,14 @@ _bss_zero:
/* hartid 0 is mandated by ISA */
li t1, 0
_scratch_init:
/*
* The following registers hold values that are computed before
* entering this block, and should remain unchanged.
*
* t3 -> the firmware end address
* s7 -> HART count
* s8 -> HART stack size
*/
add tp, t3, zero
mul a5, s8, t1
sub tp, tp, a5
@@ -287,10 +295,7 @@ _scratch_init:
/* Initialize scratch space */
/* Store fw_start and fw_size in scratch space */
lla a4, _fw_start
lla a5, _fw_end
mul t0, s7, s8
add a5, a5, t0
sub a5, a5, a4
sub a5, t3, a4
REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp)
REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp)
/* Store next arg1 in scratch space */
@@ -344,8 +349,7 @@ _scratch_init:
* previous booting stage.
*/
beqz a1, _fdt_reloc_done
/* Mask values in a3 and a4 */
li a3, ~(__SIZEOF_POINTER__ - 1)
/* Mask values in a4 */
li a4, 0xff
/* t1 = destination FDT start address */
MOV_3R s0, a0, s1, a1, s2, a2
@@ -354,10 +358,8 @@ _scratch_init:
MOV_3R a0, s0, a1, s1, a2, s2
beqz t1, _fdt_reloc_done
beq t1, a1, _fdt_reloc_done
and t1, t1, a3
/* t0 = source FDT start address */
add t0, a1, zero
and t0, t0, a3
/* t2 = source FDT size in big-endian */
#if __riscv_xlen == 64
lwu t2, 4(t0)
@@ -501,6 +503,7 @@ _skip_trap_exit_rv32_hyp:
/* We don't expect to reach here hence just hang */
j _start_hang
.data
.align 3
#ifdef FW_PIC
_runtime_offset:
@@ -553,12 +556,39 @@ _start_hang:
.section .entry, "ax", %progbits
.align 3
.globl fw_platform_init
.weak fw_platform_init
fw_platform_init:
add a0, a1, zero
ret
/* Map implicit memcpy() added by compiler to sbi_memcpy() */
.section .text
.align 3
.globl memcpy
memcpy:
tail sbi_memcpy
/* Map implicit memset() added by compiler to sbi_memset() */
.section .text
.align 3
.globl memset
memset:
tail sbi_memset
/* Map implicit memmove() added by compiler to sbi_memmove() */
.section .text
.align 3
.globl memmove
memmove:
tail sbi_memmove
/* Map implicit memcmp() added by compiler to sbi_memcmp() */
.section .text
.align 3
.globl memcmp
memcmp:
tail sbi_memcmp
.macro TRAP_SAVE_AND_SETUP_SP_T0
/* Swap TP and MSCRATCH */
csrrw tp, CSR_MSCRATCH, tp

View File

@@ -8,7 +8,7 @@
*/
. = FW_TEXT_START;
/* Don't add any section between FW_TEXT_START and _fw_start */
PROVIDE(_fw_start = .);
. = ALIGN(0x1000); /* Need this to create proper sections */

View File

@@ -36,7 +36,7 @@ fw_boot_hart:
bgt a0, a1, _bad_dynamic_info
/* Read boot HART id */
li a1, 0x2
li a1, FW_DYNAMIC_INFO_VERSION_2
blt a0, a1, 2f
REG_L a0, FW_DYNAMIC_INFO_BOOT_HART_OFFSET(a2)
ret
@@ -57,14 +57,6 @@ fw_save_info:
lla a4, _dynamic_next_arg1
REG_S a1, (a4)
/* Sanity checks */
li a4, FW_DYNAMIC_INFO_MAGIC_VALUE
REG_L a3, FW_DYNAMIC_INFO_MAGIC_OFFSET(a2)
bne a3, a4, _bad_dynamic_info
li a4, FW_DYNAMIC_INFO_VERSION_MAX
REG_L a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
bgt a3, a4, _bad_dynamic_info
/* Save version == 0x1 fields */
lla a4, _dynamic_next_addr
REG_L a3, FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET(a2)
@@ -77,7 +69,7 @@ fw_save_info:
REG_S a3, (a4)
/* Save version == 0x2 fields */
li a4, 0x2
li a4, FW_DYNAMIC_INFO_VERSION_2
REG_L a3, FW_DYNAMIC_INFO_VERSION_OFFSET(a2)
blt a3, a4, 2f
lla a4, _dynamic_boot_hart

View File

@@ -14,14 +14,14 @@ firmware-asflags-y +=
firmware-ldflags-y +=
ifndef FW_PIC
FW_PIC := y
FW_PIC := $(OPENSBI_LD_PIE)
endif
ifeq ($(FW_PIC),y)
firmware-genflags-y += -DFW_PIC
firmware-asflags-y += -fpic
firmware-cflags-y += -fPIE -pie
firmware-ldflags-y += -Wl,--no-dynamic-linker
firmware-ldflags-y += -Wl,--no-dynamic-linker -Wl,-pie
endif
ifdef FW_TEXT_START

View File

@@ -9,24 +9,25 @@
#include <sbi/sbi_ecall_interface.h>
#define SBI_ECALL(__num, __a0, __a1, __a2) \
#define SBI_ECALL(__eid, __fid, __a0, __a1, __a2) \
({ \
register unsigned long a0 asm("a0") = (unsigned long)(__a0); \
register unsigned long a1 asm("a1") = (unsigned long)(__a1); \
register unsigned long a2 asm("a2") = (unsigned long)(__a2); \
register unsigned long a7 asm("a7") = (unsigned long)(__num); \
register unsigned long a6 asm("a6") = (unsigned long)(__fid); \
register unsigned long a7 asm("a7") = (unsigned long)(__eid); \
asm volatile("ecall" \
: "+r"(a0) \
: "r"(a1), "r"(a2), "r"(a7) \
: "r"(a1), "r"(a2), "r"(a6), "r"(a7) \
: "memory"); \
a0; \
})
#define SBI_ECALL_0(__num) SBI_ECALL(__num, 0, 0, 0)
#define SBI_ECALL_1(__num, __a0) SBI_ECALL(__num, __a0, 0, 0)
#define SBI_ECALL_2(__num, __a0, __a1) SBI_ECALL(__num, __a0, __a1, 0)
#define SBI_ECALL_0(__eid, __fid) SBI_ECALL(__eid, __fid, 0, 0, 0)
#define SBI_ECALL_1(__eid, __fid, __a0) SBI_ECALL(__eid, __fid, __a0, 0, 0)
#define SBI_ECALL_2(__eid, __fid, __a0, __a1) SBI_ECALL(__eid, __fid, __a0, __a1, 0)
#define sbi_ecall_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, (c))
#define sbi_ecall_console_putc(c) SBI_ECALL_1(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, (c))
static inline void sbi_ecall_console_puts(const char *str)
{

View File

@@ -15,23 +15,24 @@
/* clang-format off */
/** Offset of magic member in fw_dynamic_info */
#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_POINTER__)
#define FW_DYNAMIC_INFO_MAGIC_OFFSET (0 * __SIZEOF_LONG__)
/** Offset of version member in fw_dynamic_info */
#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_POINTER__)
#define FW_DYNAMIC_INFO_VERSION_OFFSET (1 * __SIZEOF_LONG__)
/** Offset of next_addr member in fw_dynamic_info (version >= 1) */
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_POINTER__)
#define FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET (2 * __SIZEOF_LONG__)
/** Offset of next_mode member in fw_dynamic_info (version >= 1) */
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_POINTER__)
#define FW_DYNAMIC_INFO_NEXT_MODE_OFFSET (3 * __SIZEOF_LONG__)
/** Offset of options member in fw_dynamic_info (version >= 1) */
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_POINTER__)
#define FW_DYNAMIC_INFO_OPTIONS_OFFSET (4 * __SIZEOF_LONG__)
/** Offset of boot_hart member in fw_dynamic_info (version >= 2) */
#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_POINTER__)
#define FW_DYNAMIC_INFO_BOOT_HART_OFFSET (5 * __SIZEOF_LONG__)
/** Expected value of info magic ('OSBI' ascii string in hex) */
#define FW_DYNAMIC_INFO_MAGIC_VALUE 0x4942534f
/** Maximum supported info version */
#define FW_DYNAMIC_INFO_VERSION_MAX 0x2
#define FW_DYNAMIC_INFO_VERSION_2 0x2
#define FW_DYNAMIC_INFO_VERSION_MAX FW_DYNAMIC_INFO_VERSION_2
/** Possible next mode values */
#define FW_DYNAMIC_INFO_NEXT_MODE_U 0x0

View File

@@ -86,6 +86,7 @@
#define IRQ_VS_EXT 10
#define IRQ_M_EXT 11
#define IRQ_S_GEXT 12
#define IRQ_PMU_OVF 13
#define MIP_SSIP (_UL(1) << IRQ_S_SOFT)
#define MIP_VSSIP (_UL(1) << IRQ_VS_SOFT)
@@ -97,6 +98,7 @@
#define MIP_VSEIP (_UL(1) << IRQ_VS_EXT)
#define MIP_MEIP (_UL(1) << IRQ_M_EXT)
#define MIP_SGEIP (_UL(1) << IRQ_S_GEXT)
#define MIP_LCOFIP (_UL(1) << IRQ_PMU_OVF)
#define SIP_SSIP MIP_SSIP
#define SIP_STIP MIP_STIP
@@ -171,6 +173,31 @@
#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT
#endif
#if __riscv_xlen == 64
#define MHPMEVENT_OF (_UL(1) << 63)
#define MHPMEVENT_MINH (_UL(1) << 62)
#define MHPMEVENT_SINH (_UL(1) << 61)
#define MHPMEVENT_UINH (_UL(1) << 60)
#define MHPMEVENT_VSINH (_UL(1) << 59)
#define MHPMEVENT_VUINH (_UL(1) << 58)
#else
#define MHPMEVENTH_OF (_UL(1) << 31)
#define MHPMEVENTH_MINH (_ULL(1) << 30)
#define MHPMEVENTH_SINH (_ULL(1) << 29)
#define MHPMEVENTH_UINH (_ULL(1) << 28)
#define MHPMEVENTH_VSINH (_ULL(1) << 27)
#define MHPMEVENTH_VUINH (_ULL(1) << 26)
#define MHPMEVENT_MINH (MHPMEVENTH_MINH << 32)
#define MHPMEVENT_SINH (MHPMEVENTH_SINH << 32)
#define MHPMEVENT_UINH (MHPMEVENTH_UINH << 32)
#define MHPMEVENT_VSINH (MHPMEVENTH_VSINH << 32)
#define MHPMEVENT_VUINH (MHPMEVENTH_VUINH << 32)
#endif
#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000)
/* ===== User-level CSRs ===== */
/* User Trap Setup (N-extension) */
@@ -516,6 +543,40 @@
#define CSR_MHPMEVENT30 0x33e
#define CSR_MHPMEVENT31 0x33f
/* For RV32 */
#define CSR_MHPMEVENT3H 0x723
#define CSR_MHPMEVENT4H 0x724
#define CSR_MHPMEVENT5H 0x725
#define CSR_MHPMEVENT6H 0x726
#define CSR_MHPMEVENT7H 0x727
#define CSR_MHPMEVENT8H 0x728
#define CSR_MHPMEVENT9H 0x729
#define CSR_MHPMEVENT10H 0x72a
#define CSR_MHPMEVENT11H 0x72b
#define CSR_MHPMEVENT12H 0x72c
#define CSR_MHPMEVENT13H 0x72d
#define CSR_MHPMEVENT14H 0x72e
#define CSR_MHPMEVENT15H 0x72f
#define CSR_MHPMEVENT16H 0x730
#define CSR_MHPMEVENT17H 0x731
#define CSR_MHPMEVENT18H 0x732
#define CSR_MHPMEVENT19H 0x733
#define CSR_MHPMEVENT20H 0x734
#define CSR_MHPMEVENT21H 0x735
#define CSR_MHPMEVENT22H 0x736
#define CSR_MHPMEVENT23H 0x737
#define CSR_MHPMEVENT24H 0x738
#define CSR_MHPMEVENT25H 0x739
#define CSR_MHPMEVENT26H 0x73a
#define CSR_MHPMEVENT27H 0x73b
#define CSR_MHPMEVENT28H 0x73c
#define CSR_MHPMEVENT29H 0x73d
#define CSR_MHPMEVENT30H 0x73e
#define CSR_MHPMEVENT31H 0x73f
/* Counter Overflow CSR */
#define CSR_SCOUNTOVF 0xda0
/* Debug/Trace Registers */
#define CSR_TSELECT 0x7a0
#define CSR_TDATA1 0x7a1

View File

@@ -42,15 +42,28 @@
: "t0"); \
})
#define init_fp_reg(i) SET_F32_REG((i) << 3, 3, 0, 0)
#if __riscv_xlen == 64
#define GET_F64_REG(insn, pos, regs) \
({ \
register ulong value asm("a0") = \
SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \
register ulong value asm("a0") = SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \
ulong tmp; \
asm("1: auipc %0, %%pcrel_hi(get_f64_reg); add %0, %0, %1; jalr t0, %0, %%pcrel_lo(1b)" \
: "=&r"(tmp), "+&r"(value)::"t0"); \
sizeof(ulong) == 4 ? *(int64_t *)value : (int64_t)value; \
value; \
})
#else
#define GET_F64_REG(insn, pos, regs) \
({ \
u64 value; \
ulong offset = SHIFT_RIGHT(insn, (pos)-3) & 0xf8; \
register ulong ptr asm("a0") = (ulong)&value; \
asm ("1: auipc t1, %%pcrel_hi(get_f64_reg); add t1, t1, %2; jalr t0, t1, %%pcrel_lo(1b)" \
: "=m"(value) : "r"(ptr), "r"(offset) : "t0", "t1"); \
value; \
})
#endif
#define SET_F64_REG(insn, pos, regs, val) \
({ \
uint64_t __val = (val); \

View File

@@ -34,9 +34,9 @@ typedef struct {
#define DEFINE_SPIN_LOCK(x) \
spinlock_t SPIN_LOCK_INIT(x)
int spin_lock_check(spinlock_t *lock);
bool spin_lock_check(spinlock_t *lock);
int spin_trylock(spinlock_t *lock);
bool spin_trylock(spinlock_t *lock);
void spin_lock(spinlock_t *lock);

View File

@@ -43,6 +43,8 @@ int __printf(1, 2) sbi_printf(const char *format, ...);
int __printf(1, 2) sbi_dprintf(const char *format, ...);
void __printf(1, 2) __attribute__((noreturn)) sbi_panic(const char *format, ...);
const struct sbi_console_device *sbi_console_get_device(void);
void sbi_console_set_device(const struct sbi_console_device *dev);
@@ -51,4 +53,9 @@ struct sbi_scratch;
int sbi_console_init(struct sbi_scratch *scratch);
#define SBI_ASSERT(cond, args) do { \
if (unlikely(!(cond))) \
sbi_panic args; \
} while (0)
#endif

View File

@@ -39,6 +39,7 @@ extern struct sbi_ecall_extension ecall_ipi;
extern struct sbi_ecall_extension ecall_vendor;
extern struct sbi_ecall_extension ecall_hsm;
extern struct sbi_ecall_extension ecall_srst;
extern struct sbi_ecall_extension ecall_pmu;
u16 sbi_ecall_version_major(void);

View File

@@ -28,6 +28,7 @@
#define SBI_EXT_RFENCE 0x52464E43
#define SBI_EXT_HSM 0x48534D
#define SBI_EXT_SRST 0x53525354
#define SBI_EXT_PMU 0x504D55
/* SBI function IDs for BASE extension*/
#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0
@@ -91,6 +92,139 @@
#define SBI_SRST_RESET_REASON_NONE 0x0
#define SBI_SRST_RESET_REASON_SYSFAIL 0x1
/* SBI function IDs for PMU extension */
#define SBI_EXT_PMU_NUM_COUNTERS 0x0
#define SBI_EXT_PMU_COUNTER_GET_INFO 0x1
#define SBI_EXT_PMU_COUNTER_CFG_MATCH 0x2
#define SBI_EXT_PMU_COUNTER_START 0x3
#define SBI_EXT_PMU_COUNTER_STOP 0x4
#define SBI_EXT_PMU_COUNTER_FW_READ 0x5
/** General pmu event codes specified in SBI PMU extension */
enum sbi_pmu_hw_generic_events_t {
SBI_PMU_HW_NO_EVENT = 0,
SBI_PMU_HW_CPU_CYCLES = 1,
SBI_PMU_HW_INSTRUCTIONS = 2,
SBI_PMU_HW_CACHE_REFERENCES = 3,
SBI_PMU_HW_CACHE_MISSES = 4,
SBI_PMU_HW_BRANCH_INSTRUCTIONS = 5,
SBI_PMU_HW_BRANCH_MISSES = 6,
SBI_PMU_HW_BUS_CYCLES = 7,
SBI_PMU_HW_STALLED_CYCLES_FRONTEND = 8,
SBI_PMU_HW_STALLED_CYCLES_BACKEND = 9,
SBI_PMU_HW_REF_CPU_CYCLES = 10,
SBI_PMU_HW_GENERAL_MAX,
};
/**
* Generalized hardware cache events:
*
* { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
* { read, write, prefetch } x
* { accesses, misses }
*/
enum sbi_pmu_hw_cache_id {
SBI_PMU_HW_CACHE_L1D = 0,
SBI_PMU_HW_CACHE_L1I = 1,
SBI_PMU_HW_CACHE_LL = 2,
SBI_PMU_HW_CACHE_DTLB = 3,
SBI_PMU_HW_CACHE_ITLB = 4,
SBI_PMU_HW_CACHE_BPU = 5,
SBI_PMU_HW_CACHE_NODE = 6,
SBI_PMU_HW_CACHE_MAX,
};
enum sbi_pmu_hw_cache_op_id {
SBI_PMU_HW_CACHE_OP_READ = 0,
SBI_PMU_HW_CACHE_OP_WRITE = 1,
SBI_PMU_HW_CACHE_OP_PREFETCH = 2,
SBI_PMU_HW_CACHE_OP_MAX,
};
enum sbi_pmu_hw_cache_op_result_id {
SBI_PMU_HW_CACHE_RESULT_ACCESS = 0,
SBI_PMU_HW_CACHE_RESULT_MISS = 1,
SBI_PMU_HW_CACHE_RESULT_MAX,
};
/**
* Special "firmware" events provided by the OpenSBI, even if the hardware
* does not support performance events. These events are encoded as a raw
* event type in Linux kernel perf framework.
*/
enum sbi_pmu_fw_event_code_id {
SBI_PMU_FW_MISALIGNED_LOAD = 0,
SBI_PMU_FW_MISALIGNED_STORE = 1,
SBI_PMU_FW_ACCESS_LOAD = 2,
SBI_PMU_FW_ACCESS_STORE = 3,
SBI_PMU_FW_ILLEGAL_INSN = 4,
SBI_PMU_FW_SET_TIMER = 5,
SBI_PMU_FW_IPI_SENT = 6,
SBI_PMU_FW_IPI_RECVD = 7,
SBI_PMU_FW_FENCE_I_SENT = 8,
SBI_PMU_FW_FENCE_I_RECVD = 9,
SBI_PMU_FW_SFENCE_VMA_SENT = 10,
SBI_PMU_FW_SFENCE_VMA_RCVD = 11,
SBI_PMU_FW_SFENCE_VMA_ASID_SENT = 12,
SBI_PMU_FW_SFENCE_VMA_ASID_RCVD = 13,
SBI_PMU_FW_HFENCE_GVMA_SENT = 14,
SBI_PMU_FW_HFENCE_GVMA_RCVD = 15,
SBI_PMU_FW_HFENCE_GVMA_VMID_SENT = 16,
SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD = 17,
SBI_PMU_FW_HFENCE_VVMA_SENT = 18,
SBI_PMU_FW_HFENCE_VVMA_RCVD = 19,
SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20,
SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21,
SBI_PMU_FW_MAX,
};
/** SBI PMU event idx type */
enum sbi_pmu_event_type_id {
SBI_PMU_EVENT_TYPE_HW = 0x0,
SBI_PMU_EVENT_TYPE_HW_CACHE = 0x1,
SBI_PMU_EVENT_TYPE_HW_RAW = 0x2,
SBI_PMU_EVENT_TYPE_FW = 0xf,
SBI_PMU_EVENT_TYPE_MAX,
};
/** SBI PMU counter type */
enum sbi_pmu_ctr_type {
SBI_PMU_CTR_TYPE_HW = 0,
SBI_PMU_CTR_TYPE_FW,
};
/* Helper macros to decode event idx */
#define SBI_PMU_EVENT_IDX_OFFSET 20
#define SBI_PMU_EVENT_IDX_MASK 0xFFFFF
#define SBI_PMU_EVENT_IDX_CODE_MASK 0xFFFF
#define SBI_PMU_EVENT_IDX_TYPE_MASK 0xF0000
#define SBI_PMU_EVENT_RAW_IDX 0x20000
#define SBI_PMU_EVENT_IDX_INVALID 0xFFFFFFFF
/* Flags defined for config matching function */
#define SBI_PMU_CFG_FLAG_SKIP_MATCH (1 << 0)
#define SBI_PMU_CFG_FLAG_CLEAR_VALUE (1 << 1)
#define SBI_PMU_CFG_FLAG_AUTO_START (1 << 2)
#define SBI_PMU_CFG_FLAG_SET_VUINH (1 << 3)
#define SBI_PMU_CFG_FLAG_SET_VSINH (1 << 4)
#define SBI_PMU_CFG_FLAG_SET_UINH (1 << 5)
#define SBI_PMU_CFG_FLAG_SET_SINH (1 << 6)
#define SBI_PMU_CFG_FLAG_SET_MINH (1 << 7)
/* Flags defined for counter start function */
#define SBI_PMU_START_FLAG_SET_INIT_VALUE (1 << 0)
/* Flags defined for counter stop function */
#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
/* SBI base specification related macros */
#define SBI_SPEC_VERSION_MAJOR_OFFSET 24
#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff
@@ -107,8 +241,10 @@
#define SBI_ERR_DENIED -4
#define SBI_ERR_INVALID_ADDRESS -5
#define SBI_ERR_ALREADY_AVAILABLE -6
#define SBI_ERR_ALREADY_STARTED -7
#define SBI_ERR_ALREADY_STOPPED -8
#define SBI_LAST_ERR SBI_ERR_ALREADY_AVAILABLE
#define SBI_LAST_ERR SBI_ERR_ALREADY_STOPPED
/* clang-format on */

View File

@@ -21,6 +21,8 @@
#define SBI_EDENIED SBI_ERR_DENIED
#define SBI_EINVALID_ADDR SBI_ERR_INVALID_ADDRESS
#define SBI_EALREADY SBI_ERR_ALREADY_AVAILABLE
#define SBI_EALREADY_STARTED SBI_ERR_ALREADY_STARTED
#define SBI_EALREADY_STOPPED SBI_ERR_ALREADY_STOPPED
#define SBI_ENODEV -1000
#define SBI_ENOSYS -1001

View File

@@ -33,8 +33,8 @@ int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data);
int sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data);
void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries,
u16 entry_size);
bool sbi_fifo_is_empty(struct sbi_fifo *fifo);
bool sbi_fifo_is_full(struct sbi_fifo *fifo);
int sbi_fifo_is_empty(struct sbi_fifo *fifo);
int sbi_fifo_is_full(struct sbi_fifo *fifo);
int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
int (*fptr)(void *in, void *data));
u16 sbi_fifo_avail(struct sbi_fifo *fifo);

View File

@@ -18,8 +18,12 @@ enum sbi_hart_features {
SBI_HART_HAS_SCOUNTEREN = (1 << 0),
/** Hart has M-mode counter enable */
SBI_HART_HAS_MCOUNTEREN = (1 << 1),
/** Hart has counter inhibit CSR */
SBI_HART_HAS_MCOUNTINHIBIT = (1 << 2),
/** Hart has sscofpmf extension */
SBI_HART_HAS_SSCOFPMF = (1 << 3),
/** HART has timer csr implementation in hardware */
SBI_HART_HAS_TIME = (1 << 2),
SBI_HART_HAS_TIME = (1 << 4),
/** Last index of Hart features*/
SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_TIME,
@@ -42,6 +46,7 @@ void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
unsigned int sbi_hart_pmp_count(struct sbi_scratch *scratch);
unsigned long sbi_hart_pmp_granularity(struct sbi_scratch *scratch);
unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch);
unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch);
int sbi_hart_pmp_configure(struct sbi_scratch *scratch);
bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature);
void sbi_hart_get_features_str(struct sbi_scratch *scratch,

View File

@@ -12,13 +12,14 @@
#define __SBI_FENCE_H__
/** Invalidate Stage2 TLBs for given VMID and guest physical address */
void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa, unsigned long vmid);
void __sbi_hfence_gvma_vmid_gpa(unsigned long gpa_divby_4,
unsigned long vmid);
/** Invalidate Stage2 TLBs for given VMID */
void __sbi_hfence_gvma_vmid(unsigned long vmid);
/** Invalidate Stage2 TLBs for given guest physical address */
void __sbi_hfence_gvma_gpa(unsigned long gpa);
void __sbi_hfence_gvma_gpa(unsigned long gpa_divby_4);
/** Invalidate all possible Stage2 TLBs */
void __sbi_hfence_gvma_all(void);

View File

@@ -43,6 +43,17 @@ static inline void __sbi_list_add(struct sbi_dlist *new,
next->prev = new;
}
/**
* Checks if the list is empty or not.
* @param head List head
*
* Retruns TRUE if list is empty, FALSE otherwise.
*/
static inline bool sbi_list_empty(struct sbi_dlist *head)
{
return head->next == head;
}
/**
* Adds the new node after the given head.
* @param new New node that needs to be added to list.

View File

@@ -89,6 +89,12 @@ struct sbi_platform_operations {
/** Initialize (or populate) domains for the platform */
int (*domains_init)(void);
/** Initialize hw performance counters */
int (*pmu_init)(void);
/** Get platform specific mhpmevent value */
uint64_t (*pmu_xlate_to_mhpmevent)(uint32_t event_idx, uint64_t data);
/** Initialize the platform console */
int (*console_init)(void);
@@ -391,6 +397,39 @@ static inline int sbi_platform_domains_init(const struct sbi_platform *plat)
return 0;
}
/**
* Setup hw PMU events for the platform
*
* @param plat pointer to struct sbi_platform
*
* @return 0 on success and negative error code on failure
*/
static inline int sbi_platform_pmu_init(const struct sbi_platform *plat)
{
if (plat && sbi_platform_ops(plat)->pmu_init)
return sbi_platform_ops(plat)->pmu_init();
return 0;
}
/**
* Get the value to be written in mhpmeventx for event_idx
*
* @param plat pointer to struct sbi_platform
* @param event_idx ID of the PMU event
* @param data Additional configuration data passed from supervisor software
*
* @return expected value by the platform or 0 if platform doesn't know about
* the event
*/
static inline uint64_t sbi_platform_pmu_xlate_to_mhpmevent(const struct sbi_platform *plat,
uint32_t event_idx, uint64_t data)
{
if (plat && sbi_platform_ops(plat)->pmu_xlate_to_mhpmevent)
return sbi_platform_ops(plat)->pmu_xlate_to_mhpmevent(event_idx,
data);
return 0;
}
/**
* Initialize the platform console
*

74
include/sbi/sbi_pmu.h Normal file
View File

@@ -0,0 +1,74 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#ifndef __SBI_PMU_H__
#define __SBI_PMU_H__
#include <sbi/sbi_types.h>
#include <sbi/sbi_hartmask.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_ecall_interface.h>
/* Event related macros */
/* Maximum number of hardware events that can mapped by OpenSBI */
#define SBI_PMU_HW_EVENT_MAX 64
/* Maximum number of firmware events that can mapped by OpenSBI */
#define SBI_PMU_FW_EVENT_MAX 32
/* Counter related macros */
#define SBI_PMU_FW_CTR_MAX 16
#define SBI_PMU_HW_CTR_MAX 32
#define SBI_PMU_CTR_MAX (SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX)
#define SBI_PMU_FIXED_CTR_MASK 0x07
/** Initialize PMU */
int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot);
/** Reset PMU during hart exit */
void sbi_pmu_exit(struct sbi_scratch *scratch);
/**
* Add the hardware event to counter mapping information. This should be called
* from the platform code to update the mapping table.
* @param eidx_start Start of the event idx range for supported counters
* @param eidx_end End of the event idx range for supported counters
* @param cmap A bitmap representing counters supporting the event range
* @return 0 on success, error otherwise.
*/
int sbi_pmu_add_hw_event_counter_map(u32 eidx_start, u32 eidx_end, u32 cmap);
/**
* Add the raw hardware event selector and supported counter information. This
* should be called from the platform code to update the mapping table.
* @param info a pointer to the hardware event info
* @return 0 on success, error otherwise.
*/
int sbi_pmu_add_raw_event_counter_map(uint64_t select, uint64_t select_mask, u32 cmap);
int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval);
int sbi_pmu_ctr_stop(unsigned long cidx_base, unsigned long cidx_mask,
unsigned long flag);
int sbi_pmu_ctr_start(unsigned long cidx_base, unsigned long cidx_mask,
unsigned long flags, uint64_t ival);
int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info);
unsigned long sbi_pmu_num_ctr(void);
int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
unsigned long flags, unsigned long event_idx,
uint64_t event_data);
int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id);
#endif

View File

@@ -98,7 +98,7 @@ int sbi_scratch_init(struct sbi_scratch *scratch);
* @return zero on failure and non-zero (>= SBI_SCRATCH_EXTRA_SPACE_OFFSET)
* on success
*/
unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner);
unsigned long sbi_scratch_alloc_offset(unsigned long size);
/** Free-up extra space in sbi_scratch */
void sbi_scratch_free_offset(unsigned long offset);

View File

@@ -11,6 +11,7 @@
#define __SBI_SYSTEM_H__
#include <sbi/sbi_types.h>
#include <sbi/sbi_list.h>
/** System reset hardware device */
struct sbi_system_reset_device {
@@ -22,11 +23,21 @@ struct sbi_system_reset_device {
/** Reset the system */
void (*system_reset)(u32 reset_type, u32 reset_reason);
/** List */
struct sbi_dlist node;
};
const struct sbi_system_reset_device *sbi_system_reset_get_device(void);
static inline struct sbi_system_reset_device *to_system_reset_device(
struct sbi_dlist *node)
{
return container_of(node, struct sbi_system_reset_device, node);
}
void sbi_system_reset_set_device(const struct sbi_system_reset_device *dev);
const struct sbi_system_reset_device *sbi_system_reset_get_device(
u32 reset_type, u32 reset_reason);
void sbi_system_reset_add_device(struct sbi_system_reset_device *dev);
bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason);

View File

@@ -17,6 +17,9 @@ struct sbi_timer_device {
/** Name of the timer operations */
char name[32];
/** Frequency of timer in HZ */
unsigned long timer_freq;
/** Get free-running timer value */
u64 (*timer_value)(void);
@@ -29,6 +32,22 @@ struct sbi_timer_device {
struct sbi_scratch;
/** Generic delay loop of desired granularity */
void sbi_timer_delay_loop(ulong units, u64 unit_freq,
void (*delay_fn)(void *), void *opaque);
/** Provide delay in terms of milliseconds */
static inline void sbi_timer_mdelay(ulong msecs)
{
sbi_timer_delay_loop(msecs, 1000, NULL, NULL);
}
/** Provide delay in terms of microseconds */
static inline void sbi_timer_udelay(ulong usecs)
{
sbi_timer_delay_loop(usecs, 1000000, NULL, NULL);
}
/** Get timer value for current HART */
u64 sbi_timer_value(void);

View File

@@ -68,8 +68,14 @@ typedef unsigned long physical_size_t;
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
#ifndef __has_builtin
#define __has_builtin(...) 0
#endif
#undef offsetof
#ifdef __compiler_offsetof
#if __has_builtin(__builtin_offsetof)
#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE,MEMBER)
#elif defined(__compiler_offsetof)
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

View File

@@ -10,8 +10,8 @@
#ifndef __SBI_VERSION_H__
#define __SBI_VERSION_H__
#define OPENSBI_VERSION_MAJOR 0
#define OPENSBI_VERSION_MINOR 9
#define OPENSBI_VERSION_MAJOR 1
#define OPENSBI_VERSION_MINOR 0
/**
* OpenSBI 32-bit version with:

View File

@@ -11,12 +11,20 @@
#define __FDT_HELPER_H__
#include <sbi/sbi_types.h>
#include <sbi/sbi_scratch.h>
struct fdt_match {
const char *compatible;
void *data;
};
#define FDT_MAX_PHANDLE_ARGS 16
struct fdt_phandle_args {
int node_offset;
int args_count;
u32 args[FDT_MAX_PHANDLE_ARGS];
};
struct platform_uart_data {
unsigned long addr;
unsigned long freq;
@@ -32,13 +40,22 @@ int fdt_find_match(void *fdt, int startoff,
const struct fdt_match *match_table,
const struct fdt_match **out_match);
int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
unsigned long *size);
int fdt_parse_phandle_with_args(void *fdt, int nodeoff,
const char *prop, const char *cells_prop,
int index, struct fdt_phandle_args *out_args);
int fdt_get_node_addr_size(void *fdt, int node, int index,
uint64_t *addr, uint64_t *size);
int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid);
int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid);
int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq);
int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
struct platform_uart_data *uart);
int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
struct platform_uart_data *uart);
@@ -57,12 +74,17 @@ int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic);
int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat);
struct clint_data;
int fdt_parse_aclint_node(void *fdt, int nodeoffset, bool for_timer,
unsigned long *out_addr1, unsigned long *out_size1,
unsigned long *out_addr2, unsigned long *out_size2,
u32 *out_first_hartid, u32 *out_hart_count);
int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
struct clint_data *clint);
int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
const char *compatible);
static inline void *fdt_get_address(void)
{
return sbi_scratch_thishart_arg1_ptr();
}
#endif /* __FDT_HELPER_H__ */

View File

@@ -0,0 +1,46 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* fdt_pmu.c - Flat Device Tree PMU helper routines
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#ifndef __FDT_PMU_H__
#define __FDT_PMU_H__
#include <sbi/sbi_types.h>
/**
* Fix up the PMU node in the device tree
*
* This routine:
* 1. Disables opensbi specific properties from the DT
*
* It is recommended that platform support call this function in
* their final_init() platform operation.
*
* @param fdt device tree blob
*/
void fdt_pmu_fixup(void *fdt);
/**
* Setup PMU data from device tree
*
* @param fdt device tree blob
*
* @return 0 on success and negative error code on failure
*/
int fdt_pmu_setup(void *fdt);
/**
* Get the mhpmevent select value read from DT for a given event
* @param event_idx Event ID of the given event
*
* @return The select value read from DT or 0 if given index was not found
*/
uint64_t fdt_pmu_get_select_value(uint32_t event_idx);
#endif

View File

@@ -0,0 +1,34 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#ifndef __FDT_GPIO_H__
#define __FDT_GPIO_H__
#include <sbi_utils/gpio/gpio.h>
/** FDT based GPIO driver */
struct fdt_gpio {
const struct fdt_match *match_table;
int (*xlate)(struct gpio_chip *chip,
const struct fdt_phandle_args *pargs,
struct gpio_pin *out_pin);
int (*init)(void *fdt, int nodeoff, u32 phandle,
const struct fdt_match *match);
};
/** Get a GPIO pin using "gpios" DT property of client DT node */
int fdt_gpio_pin_get(void *fdt, int nodeoff, int index,
struct gpio_pin *out_pin);
/** Simple xlate function to convert two GPIO FDT cells into GPIO pin */
int fdt_gpio_simple_xlate(struct gpio_chip *chip,
const struct fdt_phandle_args *pargs,
struct gpio_pin *out_pin);
#endif

View File

@@ -0,0 +1,107 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#ifndef __GPIO_H__
#define __GPIO_H__
#include <sbi/sbi_types.h>
#include <sbi/sbi_list.h>
#define GPIO_LINE_DIRECTION_IN 1
#define GPIO_LINE_DIRECTION_OUT 0
/** Representation of a GPIO pin */
struct gpio_pin {
/** Pointer to the GPIO chip */
struct gpio_chip *chip;
/** Identification of GPIO pin within GPIO chip */
unsigned int offset;
/**
* Additional configuration flags of the GPIO pin desired
* by GPIO clients.
*
* NOTE: GPIO chip can have custom configuration flags.
*/
unsigned int flags;
#define GPIO_FLAG_ACTIVE_LOW 0x1
#define GPIO_FLAG_SINGLE_ENDED 0x2
#define GPIO_FLAG_OPEN_DRAIN 0x4
#define GPIO_FLAG_TRANSITORY 0x8
#define GPIO_FLAG_PULL_UP 0x10
#define GPIO_FLAG_PULL_DOWN 0x20
};
/** Representation of a GPIO chip */
struct gpio_chip {
/** Pointer to GPIO driver owning this GPIO chip */
void *driver;
/** Uniquie ID of the GPIO chip assigned by the driver */
unsigned int id;
/** Number of GPIOs supported by the GPIO chip */
unsigned int ngpio;
/**
* Get current direction of GPIO pin
*
* @return 0=output, 1=input, or negative error
*/
int (*get_direction)(struct gpio_pin *gp);
/**
* Set input direction of GPIO pin
*
* @return 0 on success and negative error code on failure
*/
int (*direction_input)(struct gpio_pin *gp);
/**
* Set output direction of GPIO pin with given output value
*
* @return 0 on success and negative error code on failure
*/
int (*direction_output)(struct gpio_pin *gp, int value);
/**
* Get current value of GPIO pin
*
* @return 0=low, 1=high, or negative error
*/
int (*get)(struct gpio_pin *gp);
/** Set output value for GPIO pin */
void (*set)(struct gpio_pin *gp, int value);
/** List */
struct sbi_dlist node;
};
static inline struct gpio_chip *to_gpio_chip(struct sbi_dlist *node)
{
return container_of(node, struct gpio_chip, node);
}
/** Find a registered GPIO chip */
struct gpio_chip *gpio_chip_find(unsigned int id);
/** Register GPIO chip */
int gpio_chip_add(struct gpio_chip *gc);
/** Un-register GPIO chip */
void gpio_chip_remove(struct gpio_chip *gc);
/** Get current direction of GPIO pin */
int gpio_get_direction(struct gpio_pin *gp);
/** Set input direction of GPIO pin */
int gpio_direction_input(struct gpio_pin *gp);
/** Set output direction of GPIO pin */
int gpio_direction_output(struct gpio_pin *gp, int value);
/** Get current value of GPIO pin */
int gpio_get(struct gpio_pin *gp);
/** Set output value of GPIO pin */
int gpio_set(struct gpio_pin *gp, int value);
#endif

View File

@@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 YADRO
*
* Authors:
* Nikita Shubin <n.shubin@yadro.com>
*/
#ifndef __FDT_I2C_H__
#define __FDT_I2C_H__
#include <sbi_utils/i2c/i2c.h>
/** FDT based I2C adapter driver */
struct fdt_i2c_adapter {
const struct fdt_match *match_table;
int (*init)(void *fdt, int nodeoff,
const struct fdt_match *match);
};
/** Get I2C adapter identified by nodeoff */
int fdt_i2c_adapter_get(void *fdt, int nodeoff,
struct i2c_adapter **out_adapter);
#endif

View File

@@ -0,0 +1,85 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 YADRO
*
* Authors:
* Nikita Shubin <n.shubin@yadro.com>
*/
#ifndef __I2C_H__
#define __I2C_H__
#include <sbi/sbi_types.h>
#include <sbi/sbi_list.h>
/** Representation of a I2C adapter */
struct i2c_adapter {
/** Pointer to I2C driver owning this I2C adapter */
void *driver;
/** Unique ID of the I2C adapter assigned by the driver */
int id;
/**
* Send buffer to given address, register
*
* @return 0 on success and negative error code on failure
*/
int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
uint8_t *buffer, int len);
/**
* Read buffer from given address, register
*
* @return 0 on success and negative error code on failure
*/
int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
uint8_t *buffer, int len);
/** List */
struct sbi_dlist node;
};
static inline struct i2c_adapter *to_i2c_adapter(struct sbi_dlist *node)
{
return container_of(node, struct i2c_adapter, node);
}
/** Find a registered I2C adapter */
struct i2c_adapter *i2c_adapter_find(int id);
/** Register I2C adapter */
int i2c_adapter_add(struct i2c_adapter *ia);
/** Un-register I2C adapter */
void i2c_adapter_remove(struct i2c_adapter *ia);
/** Send to device on I2C adapter bus */
int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
uint8_t *buffer, int len);
/** Read from device on I2C adapter bus */
int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
uint8_t *buffer, int len);
static inline int i2c_adapter_reg_write(struct i2c_adapter *ia, uint8_t addr,
uint8_t reg, uint8_t val)
{
return i2c_adapter_write(ia, addr, reg, &val, 1);
}
static inline int i2c_adapter_reg_read(struct i2c_adapter *ia, uint8_t addr,
uint8_t reg, uint8_t *val)
{
uint8_t buf;
int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
if (ret)
return ret;
*val = buf;
return 0;
}
#endif

View File

@@ -0,0 +1,33 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#ifndef __IPI_ACLINT_MSWI_H__
#define __IPI_ACLINT_MSWI_H__
#include <sbi/sbi_types.h>
#define ACLINT_MSWI_ALIGN 0x1000
#define ACLINT_MSWI_SIZE 0x4000
#define ACLINT_MSWI_MAX_HARTS 4095
#define CLINT_MSWI_OFFSET 0x0000
struct aclint_mswi_data {
/* Public details */
unsigned long addr;
unsigned long size;
u32 first_hartid;
u32 hart_count;
};
int aclint_mswi_warm_init(void);
int aclint_mswi_cold_init(struct aclint_mswi_data *mswi);
#endif

View File

@@ -17,6 +17,16 @@ struct fdt_reset {
int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
};
int fdt_reset_init(void);
/**
* fdt_reset_driver_init() - initialize reset driver based on the device-tree
*/
int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv);
/**
* fdt_reset_init() - initialize reset drivers based on the device-tree
*
* This function shall be invoked in final init.
*/
void fdt_reset_init(void);
#endif

View File

@@ -0,0 +1,17 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Cobham Gaisler AB.
*
* Authors:
* Daniel Cederman <cederman@gaisler.com>
*/
#ifndef __SERIAL_GAISLER_APBUART_H__
#define __SERIAL_GAISLER_APBUART_H__
#include <sbi/sbi_types.h>
int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
#endif

View File

@@ -0,0 +1,17 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Gabriel Somlo
*
* Authors:
* Gabriel Somlo <gsomlo@gmail.com>
*/
#ifndef __SERIAL_LITEX_UART_H__
#define __SERIAL_LITEX_UART_H__
#include <sbi/sbi_types.h>
int litex_uart_init(unsigned long base);
#endif

View File

@@ -1,41 +0,0 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#ifndef __SYS_CLINT_H__
#define __SYS_CLINT_H__
#include <sbi/sbi_types.h>
struct clint_data {
/* Public details */
unsigned long addr;
u32 first_hartid;
u32 hart_count;
bool has_64bit_mmio;
/* Private details (initialized and used by CLINT library)*/
u32 *ipi;
struct clint_data *time_delta_reference;
unsigned long time_delta_computed;
u64 time_delta;
u64 *time_val;
u64 *time_cmp;
u64 (*time_rd)(volatile u64 *addr);
void (*time_wr)(u64 value, volatile u64 *addr);
};
int clint_warm_ipi_init(void);
int clint_cold_ipi_init(struct clint_data *clint);
int clint_warm_timer_init(void);
int clint_cold_timer_init(struct clint_data *clint,
struct clint_data *reference);
#endif

View File

@@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#ifndef __TIMER_ACLINT_MTIMER_H__
#define __TIMER_ACLINT_MTIMER_H__
#include <sbi/sbi_types.h>
#define ACLINT_MTIMER_ALIGN 0x8
#define ACLINT_MTIMER_MAX_HARTS 4095
#define ACLINT_DEFAULT_MTIME_OFFSET 0x7ff8
#define ACLINT_DEFAULT_MTIME_SIZE 0x8
#define ACLINT_DEFAULT_MTIMECMP_OFFSET 0x0000
#define ACLINT_DEFAULT_MTIMECMP_SIZE 0x7ff8
#define CLINT_MTIMER_OFFSET 0x4000
struct aclint_mtimer_data {
/* Public details */
unsigned long mtime_freq;
unsigned long mtime_addr;
unsigned long mtime_size;
unsigned long mtimecmp_addr;
unsigned long mtimecmp_size;
u32 first_hartid;
u32 hart_count;
bool has_64bit_mmio;
bool has_shared_mtime;
/* Private details (initialized and used by ACLINT MTIMER library) */
struct aclint_mtimer_data *time_delta_reference;
unsigned long time_delta_computed;
u64 (*time_rd)(volatile u64 *addr);
void (*time_wr)(bool timecmp, u64 value, volatile u64 *addr);
};
void aclint_mtimer_sync(struct aclint_mtimer_data *mt);
void aclint_mtimer_set_reference(struct aclint_mtimer_data *mt,
struct aclint_mtimer_data *ref);
int aclint_mtimer_warm_init(void);
int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt,
struct aclint_mtimer_data *reference);
#endif

View File

@@ -20,6 +20,7 @@ libsbi-objs-y += sbi_ecall.o
libsbi-objs-y += sbi_ecall_base.o
libsbi-objs-y += sbi_ecall_hsm.o
libsbi-objs-y += sbi_ecall_legacy.o
libsbi-objs-y += sbi_ecall_pmu.o
libsbi-objs-y += sbi_ecall_replace.o
libsbi-objs-y += sbi_ecall_vendor.o
libsbi-objs-y += sbi_emulate_csr.o
@@ -33,6 +34,7 @@ libsbi-objs-y += sbi_init.o
libsbi-objs-y += sbi_ipi.o
libsbi-objs-y += sbi_misaligned_ldst.o
libsbi-objs-y += sbi_platform.o
libsbi-objs-y += sbi_pmu.o
libsbi-objs-y += sbi_scratch.o
libsbi-objs-y += sbi_string.o
libsbi-objs-y += sbi_system.o

View File

@@ -11,6 +11,7 @@
#include <sbi/riscv_encoding.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_console.h>
/* determine CPU extension, return non-zero support */
int misa_extension_imp(char ext)
@@ -75,6 +76,8 @@ void misa_string(int xlen, char *out, unsigned int out_sz)
out[pos++] = '8';
break;
default:
sbi_panic("%s: Unknown misa.MXL encoding %d",
__func__, xlen);
return;
}
}
@@ -118,7 +121,32 @@ unsigned long csr_read_num(int csr_num)
switch (csr_num) {
switchcase_csr_read_16(CSR_PMPCFG0, ret)
switchcase_csr_read_64(CSR_PMPADDR0, ret)
switchcase_csr_read(CSR_MCYCLE, ret)
switchcase_csr_read(CSR_MINSTRET, ret)
switchcase_csr_read(CSR_MHPMCOUNTER3, ret)
switchcase_csr_read_4(CSR_MHPMCOUNTER4, ret)
switchcase_csr_read_8(CSR_MHPMCOUNTER8, ret)
switchcase_csr_read_16(CSR_MHPMCOUNTER16, ret)
switchcase_csr_read(CSR_MCOUNTINHIBIT, ret)
switchcase_csr_read(CSR_MHPMEVENT3, ret)
switchcase_csr_read_4(CSR_MHPMEVENT4, ret)
switchcase_csr_read_8(CSR_MHPMEVENT8, ret)
switchcase_csr_read_16(CSR_MHPMEVENT16, ret)
#if __riscv_xlen == 32
switchcase_csr_read(CSR_MCYCLEH, ret)
switchcase_csr_read(CSR_MINSTRETH, ret)
switchcase_csr_read(CSR_MHPMCOUNTER3H, ret)
switchcase_csr_read_4(CSR_MHPMCOUNTER4H, ret)
switchcase_csr_read_8(CSR_MHPMCOUNTER8H, ret)
switchcase_csr_read_16(CSR_MHPMCOUNTER16H, ret)
switchcase_csr_read(CSR_MHPMEVENT3H, ret)
switchcase_csr_read_4(CSR_MHPMEVENT4H, ret)
switchcase_csr_read_8(CSR_MHPMEVENT8H, ret)
switchcase_csr_read_16(CSR_MHPMEVENT16H, ret)
#endif
default:
sbi_panic("%s: Unknown CSR %#x", __func__, csr_num);
break;
};
@@ -161,7 +189,32 @@ void csr_write_num(int csr_num, unsigned long val)
switch (csr_num) {
switchcase_csr_write_16(CSR_PMPCFG0, val)
switchcase_csr_write_64(CSR_PMPADDR0, val)
switchcase_csr_write(CSR_MCYCLE, val)
switchcase_csr_write(CSR_MINSTRET, val)
switchcase_csr_write(CSR_MHPMCOUNTER3, val)
switchcase_csr_write_4(CSR_MHPMCOUNTER4, val)
switchcase_csr_write_8(CSR_MHPMCOUNTER8, val)
switchcase_csr_write_16(CSR_MHPMCOUNTER16, val)
#if __riscv_xlen == 32
switchcase_csr_write(CSR_MCYCLEH, val)
switchcase_csr_write(CSR_MINSTRETH, val)
switchcase_csr_write(CSR_MHPMCOUNTER3H, val)
switchcase_csr_write_4(CSR_MHPMCOUNTER4H, val)
switchcase_csr_write_8(CSR_MHPMCOUNTER8H, val)
switchcase_csr_write_16(CSR_MHPMCOUNTER16H, val)
switchcase_csr_write(CSR_MHPMEVENT3H, val)
switchcase_csr_write_4(CSR_MHPMEVENT4H, val)
switchcase_csr_write_8(CSR_MHPMEVENT8H, val)
switchcase_csr_write_16(CSR_MHPMEVENT16H, val)
#endif
switchcase_csr_write(CSR_MCOUNTINHIBIT, val)
switchcase_csr_write(CSR_MHPMEVENT3, val)
switchcase_csr_write_4(CSR_MHPMEVENT4, val)
switchcase_csr_write_8(CSR_MHPMEVENT8, val)
switchcase_csr_write_16(CSR_MHPMEVENT16, val)
default:
sbi_panic("%s: Unknown CSR %#x", __func__, csr_num);
break;
};
@@ -178,6 +231,9 @@ static unsigned long ctz(unsigned long x)
{
unsigned long ret = 0;
if (x == 0)
return 8 * sizeof(x);
while (!(x & 1UL)) {
ret++;
x = x >> 1;
@@ -205,14 +261,12 @@ int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
pmpcfg_shift = (n & 7) << 3;
#else
pmpcfg_csr = -1;
pmpcfg_shift = -1;
return SBI_ENOTSUPP;
#endif
pmpaddr_csr = CSR_PMPADDR0 + n;
if (pmpcfg_csr < 0 || pmpcfg_shift < 0)
return SBI_ENOTSUPP;
/* encode PMP config */
prot &= ~PMP_A;
prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
cfgmask = ~(0xffUL << pmpcfg_shift);
pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask);
@@ -258,12 +312,9 @@ int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
pmpcfg_shift = (n & 7) << 3;
#else
pmpcfg_csr = -1;
pmpcfg_shift = -1;
return SBI_ENOTSUPP;
#endif
pmpaddr_csr = CSR_PMPADDR0 + n;
if (pmpcfg_csr < 0 || pmpcfg_shift < 0)
return SBI_ENOTSUPP;
/* decode PMP config */
cfgmask = (0xffUL << pmpcfg_shift);

View File

@@ -8,7 +8,7 @@
#include <sbi/riscv_barrier.h>
#include <sbi/riscv_locks.h>
static inline int spin_lock_unlocked(spinlock_t lock)
static inline bool spin_lock_unlocked(spinlock_t lock)
{
return lock.owner == lock.next;
}
@@ -19,7 +19,7 @@ bool spin_lock_check(spinlock_t *lock)
return !spin_lock_unlocked(*lock);
}
int spin_trylock(spinlock_t *lock)
bool spin_trylock(spinlock_t *lock)
{
unsigned long inc = 1u << TICKET_SHIFT;
unsigned long mask = 0xffffu << TICKET_SHIFT;
@@ -42,7 +42,7 @@ int spin_trylock(spinlock_t *lock)
: "r"(inc), "r"(mask), "I"(TICKET_SHIFT)
: "memory");
return !l0;
return l0 == 0;
}
void spin_lock(spinlock_t *lock)

View File

@@ -9,6 +9,7 @@
#include <sbi/riscv_locks.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_scratch.h>
@@ -387,13 +388,29 @@ int sbi_dprintf(const char *format, ...)
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
va_start(args, format);
if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS)
if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS) {
spin_lock(&console_out_lock);
retval = print(NULL, NULL, format, args);
spin_unlock(&console_out_lock);
}
va_end(args);
return retval;
}
void sbi_panic(const char *format, ...)
{
va_list args;
spin_lock(&console_out_lock);
va_start(args, format);
print(NULL, NULL, format, args);
va_end(args);
spin_unlock(&console_out_lock);
sbi_hart_hang();
}
const struct sbi_console_device *sbi_console_get_device(void)
{
return console_dev;

View File

@@ -295,7 +295,7 @@ static int sanitize_domain(const struct sbi_platform *plat,
/* Check next address and next mode*/
if (!sbi_domain_check_addr(dom, dom->next_addr, dom->next_mode,
SBI_DOMAIN_EXECUTE)) {
sbi_printf("%s: %s next booting stage addres 0x%lx can't "
sbi_printf("%s: %s next booting stage address 0x%lx can't "
"execute\n", __func__, dom->name, dom->next_addr);
return SBI_EINVAL;
}
@@ -328,11 +328,7 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
rend = (reg->order < __riscv_xlen) ?
rstart + ((1UL << reg->order) - 1) : -1UL;
#if __riscv_xlen == 32
sbi_printf("Domain%d Region%02d %s: 0x%08lx-0x%08lx ",
#else
sbi_printf("Domain%d Region%02d %s: 0x%016lx-0x%016lx ",
#endif
sbi_printf("Domain%d Region%02d %s: 0x%" PRILX "-0x%" PRILX " ",
dom->index, i, suffix, rstart, rend);
k = 0;
@@ -351,18 +347,10 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
i++;
}
#if __riscv_xlen == 32
sbi_printf("Domain%d Next Address%s: 0x%08lx\n",
#else
sbi_printf("Domain%d Next Address%s: 0x%016lx\n",
#endif
sbi_printf("Domain%d Next Address%s: 0x%" PRILX "\n",
dom->index, suffix, dom->next_addr);
#if __riscv_xlen == 32
sbi_printf("Domain%d Next Arg1 %s: 0x%08lx\n",
#else
sbi_printf("Domain%d Next Arg1 %s: 0x%016lx\n",
#endif
sbi_printf("Domain%d Next Arg1 %s: 0x%" PRILX "\n",
dom->index, suffix, dom->next_arg1);
sbi_printf("Domain%d Next Mode %s: ", dom->index, suffix);
@@ -511,7 +499,8 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg)
if (!nreg1->order)
continue;
if ((nreg->base + BIT(nreg->order)) == nreg1->base &&
if (!(nreg->base & (BIT(nreg->order + 1) - 1)) &&
(nreg->base + BIT(nreg->order)) == nreg1->base &&
nreg->order == nreg1->order &&
nreg->flags == nreg1->flags) {
nreg->order++;

View File

@@ -162,6 +162,9 @@ int sbi_ecall_init(void)
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_srst);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_pmu);
if (ret)
return ret;
ret = sbi_ecall_register_extension(&ecall_legacy);

87
lib/sbi/sbi_ecall_pmu.c Normal file
View File

@@ -0,0 +1,87 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <sbi/sbi_ecall.h>
#include <sbi/sbi_ecall_interface.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_trap.h>
#include <sbi/sbi_version.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_scratch.h>
#include <sbi/riscv_asm.h>
static int sbi_ecall_pmu_handler(unsigned long extid, unsigned long funcid,
const struct sbi_trap_regs *regs,
unsigned long *out_val,
struct sbi_trap_info *out_trap)
{
int ret = 0;
uint64_t temp;
switch (funcid) {
case SBI_EXT_PMU_NUM_COUNTERS:
ret = sbi_pmu_num_ctr();
if (ret >= 0) {
*out_val = ret;
ret = 0;
}
break;
case SBI_EXT_PMU_COUNTER_GET_INFO:
ret = sbi_pmu_ctr_get_info(regs->a0, out_val);
break;
case SBI_EXT_PMU_COUNTER_CFG_MATCH:
#if __riscv_xlen == 32
temp = ((uint64_t)regs->a5 << 32) | regs->a4;
#else
temp = regs->a4;
#endif
ret = sbi_pmu_ctr_cfg_match(regs->a0, regs->a1, regs->a2,
regs->a3, temp);
if (ret >= 0) {
*out_val = ret;
ret = 0;
}
break;
case SBI_EXT_PMU_COUNTER_FW_READ:
ret = sbi_pmu_ctr_read(regs->a0, out_val);
break;
case SBI_EXT_PMU_COUNTER_START:
#if __riscv_xlen == 32
temp = ((uint64_t)regs->a4 << 32) | regs->a3;
#else
temp = regs->a3;
#endif
ret = sbi_pmu_ctr_start(regs->a0, regs->a1, regs->a2, temp);
break;
case SBI_EXT_PMU_COUNTER_STOP:
ret = sbi_pmu_ctr_stop(regs->a0, regs->a1, regs->a2);
break;
default:
ret = SBI_ENOTSUPP;
};
return ret;
}
static int sbi_ecall_pmu_probe(unsigned long extid, unsigned long *out_val)
{
/* PMU extension is always enabled */
*out_val = 1;
return 0;
}
struct sbi_ecall_extension ecall_pmu = {
.extid_start = SBI_EXT_PMU,
.extid_end = SBI_EXT_PMU,
.handle = sbi_ecall_pmu_handler,
.probe = sbi_ecall_pmu_probe,
};

View File

@@ -54,8 +54,8 @@ static int sbi_ecall_rfence_handler(unsigned long extid, unsigned long funcid,
struct sbi_tlb_info tlb_info;
u32 source_hart = current_hartid();
if (funcid >= SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA &&
funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID)
if (funcid >= SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID &&
funcid <= SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA)
if (!misa_extension('H'))
return SBI_ENOTSUPP;
@@ -151,7 +151,7 @@ static int sbi_ecall_srst_handler(unsigned long extid, unsigned long funcid,
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
break;
default:
return SBI_ENOTSUPP;
return SBI_EINVAL;
}
switch (regs->a1) {
@@ -159,7 +159,7 @@ static int sbi_ecall_srst_handler(unsigned long extid, unsigned long funcid,
case SBI_SRST_RESET_REASON_SYSFAIL:
break;
default:
return SBI_ENOTSUPP;
return SBI_EINVAL;
}
if (sbi_system_reset_supported(regs->a0, regs->a1))

View File

@@ -43,10 +43,13 @@ u16 sbi_fifo_avail(struct sbi_fifo *fifo)
return ret;
}
bool sbi_fifo_is_full(struct sbi_fifo *fifo)
int sbi_fifo_is_full(struct sbi_fifo *fifo)
{
bool ret;
if (!fifo)
return SBI_EINVAL;
spin_lock(&fifo->qlock);
ret = __sbi_fifo_is_full(fifo);
spin_unlock(&fifo->qlock);
@@ -75,10 +78,13 @@ static inline bool __sbi_fifo_is_empty(struct sbi_fifo *fifo)
return (fifo->avail == 0) ? TRUE : FALSE;
}
bool sbi_fifo_is_empty(struct sbi_fifo *fifo)
int sbi_fifo_is_empty(struct sbi_fifo *fifo)
{
bool ret;
if (!fifo)
return SBI_EINVAL;
spin_lock(&fifo->qlock);
ret = __sbi_fifo_is_empty(fifo);
spin_unlock(&fifo->qlock);
@@ -118,7 +124,7 @@ bool sbi_fifo_reset(struct sbi_fifo *fifo)
int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
int (*fptr)(void *in, void *data))
{
int i, index = 0;
int i, index;
int ret = SBI_FIFO_UNCHANGED;
void *entry;
@@ -135,7 +141,7 @@ int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
for (i = 0; i < fifo->avail; i++) {
index = fifo->tail + i;
if (index >= fifo->num_entries)
index = index - fifo->num_entries;
index -= fifo->num_entries;
entry = (void *)fifo->queue + (u32)index * fifo->entry_size;
ret = fptr(in, entry);

View File

@@ -33,6 +33,7 @@ struct hart_features {
unsigned int pmp_addr_bits;
unsigned long pmp_gran;
unsigned int mhpm_count;
unsigned int mhpm_bits;
};
static unsigned long hart_features_offset;
@@ -50,13 +51,23 @@ static void mstatus_init(struct sbi_scratch *scratch)
csr_write(CSR_MSTATUS, mstatus_val);
/* Enable user/supervisor use of perf counters */
/* Disable user mode usage of all perf counters except default ones (CY, TM, IR) */
if (misa_extension('S') &&
sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
csr_write(CSR_SCOUNTEREN, -1);
csr_write(CSR_SCOUNTEREN, 7);
/**
* OpenSBI doesn't use any PMU counters in M-mode.
* Supervisor mode usage for all counters are enabled by default
* But counters will not run until mcountinhibit is set.
*/
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN))
csr_write(CSR_MCOUNTEREN, -1);
/* All programmable counters will start running at runtime after S-mode request */
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
/* Disable all interrupts */
csr_write(CSR_MIE, 0);
@@ -97,6 +108,9 @@ static int delegate_traps(struct sbi_scratch *scratch)
/* Send M-mode interrupts and most exceptions to S-mode */
interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
interrupts |= MIP_LCOFIP;
exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) |
(1U << CAUSE_USER_ECALL);
if (sbi_platform_has_mfaults_delegation(plat))
@@ -132,17 +146,10 @@ void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
/* No delegation possible as mideleg does not exist*/
return;
#if __riscv_xlen == 32
sbi_printf("%sMIDELEG%s: 0x%08lx\n",
sbi_printf("%sMIDELEG%s: 0x%" PRILX "\n",
prefix, suffix, csr_read(CSR_MIDELEG));
sbi_printf("%sMEDELEG%s: 0x%08lx\n",
sbi_printf("%sMEDELEG%s: 0x%" PRILX "\n",
prefix, suffix, csr_read(CSR_MEDELEG));
#else
sbi_printf("%sMIDELEG%s: 0x%016lx\n",
prefix, suffix, csr_read(CSR_MIDELEG));
sbi_printf("%sMEDELEG%s: 0x%016lx\n",
prefix, suffix, csr_read(CSR_MEDELEG));
#endif
}
unsigned int sbi_hart_mhpm_count(struct sbi_scratch *scratch)
@@ -177,6 +184,14 @@ unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch)
return hfeatures->pmp_addr_bits;
}
unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch)
{
struct hart_features *hfeatures =
sbi_scratch_offset_ptr(scratch, hart_features_offset);
return hfeatures->mhpm_bits;
}
int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
{
struct sbi_domain_memregion *reg;
@@ -211,7 +226,7 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
else {
sbi_printf("Can not configure pmp for domain %s", dom->name);
sbi_printf("because memory region address %lx or size %lx is not in range\n",
sbi_printf(" because memory region address %lx or size %lx is not in range\n",
reg->base, reg->order);
}
}
@@ -259,6 +274,12 @@ static inline char *sbi_hart_feature_id2string(unsigned long feature)
case SBI_HART_HAS_MCOUNTEREN:
fstr = "mcounteren";
break;
case SBI_HART_HAS_MCOUNTINHIBIT:
fstr = "mcountinhibit";
break;
case SBI_HART_HAS_SSCOFPMF:
fstr = "sscofpmf";
break;
case SBI_HART_HAS_TIME:
fstr = "time";
break;
@@ -317,7 +338,11 @@ static unsigned long hart_pmp_get_allowed_addr(void)
unsigned long val = 0;
struct sbi_trap_info trap = {0};
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK); \
csr_write_allowed(CSR_PMPCFG0, (ulong)&trap, 0);
if (trap.cause)
return 0;
csr_write_allowed(CSR_PMPADDR0, (ulong)&trap, PMP_ADDR_MASK);
if (!trap.cause) {
val = csr_read_allowed(CSR_PMPADDR0, (ulong)&trap);
if (trap.cause)
@@ -327,6 +352,37 @@ static unsigned long hart_pmp_get_allowed_addr(void)
return val;
}
static int hart_pmu_get_allowed_bits(void)
{
unsigned long val = ~(0UL);
struct sbi_trap_info trap = {0};
int num_bits = 0;
/**
* It is assumed that platforms will implement same number of bits for
* all the performance counters including mcycle/minstret.
*/
csr_write_allowed(CSR_MHPMCOUNTER3, (ulong)&trap, val);
if (!trap.cause) {
val = csr_read_allowed(CSR_MHPMCOUNTER3, (ulong)&trap);
if (trap.cause)
return 0;
}
num_bits = __fls(val) + 1;
#if __riscv_xlen == 32
csr_write_allowed(CSR_MHPMCOUNTER3H, (ulong)&trap, val);
if (!trap.cause) {
val = csr_read_allowed(CSR_MHPMCOUNTER3H, (ulong)&trap);
if (trap.cause)
return num_bits;
}
num_bits += __fls(val) + 1;
#endif
return num_bits;
}
static void hart_detect_features(struct sbi_scratch *scratch)
{
struct sbi_trap_info trap = {0};
@@ -392,9 +448,17 @@ __pmp_skip:
/* Detect number of MHPM counters */
__check_csr(CSR_MHPMCOUNTER3, 0, 1UL, mhpm_count, __mhpm_skip);
hfeatures->mhpm_bits = hart_pmu_get_allowed_bits();
__check_csr_4(CSR_MHPMCOUNTER4, 0, 1UL, mhpm_count, __mhpm_skip);
__check_csr_8(CSR_MHPMCOUNTER8, 0, 1UL, mhpm_count, __mhpm_skip);
__check_csr_16(CSR_MHPMCOUNTER16, 0, 1UL, mhpm_count, __mhpm_skip);
/**
* No need to check for MHPMCOUNTERH for RV32 as they are expected to be
* implemented if MHPMCOUNTER is implemented.
*/
__mhpm_skip:
#undef __check_csr_64
@@ -421,6 +485,23 @@ __mhpm_skip:
hfeatures->features |= SBI_HART_HAS_MCOUNTEREN;
}
/* Detect if hart supports MCOUNTINHIBIT feature */
val = csr_read_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap);
if (!trap.cause) {
csr_write_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap, val);
if (!trap.cause)
hfeatures->features |= SBI_HART_HAS_MCOUNTINHIBIT;
}
/* Counter overflow/filtering is not useful without mcounter/inhibit */
if (hfeatures->features & SBI_HART_HAS_MCOUNTINHIBIT &&
hfeatures->features & SBI_HART_HAS_MCOUNTEREN) {
/* Detect if hart supports sscofpmf */
csr_read_allowed(CSR_SCOUNTOVF, (unsigned long)&trap);
if (!trap.cause)
hfeatures->features |= SBI_HART_HAS_SSCOFPMF;
}
/* Detect if hart supports time CSR */
csr_read_allowed(CSR_TIME, (unsigned long)&trap);
if (!trap.cause)
@@ -451,8 +532,7 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
sbi_hart_expected_trap = &__sbi_expected_trap_hext;
hart_features_offset = sbi_scratch_alloc_offset(
sizeof(struct hart_features),
"HART_FEATURES");
sizeof(struct hart_features));
if (!hart_features_offset)
return SBI_ENOMEM;
}

View File

@@ -27,7 +27,7 @@
.global __sbi_hfence_gvma_vmid_gpa
__sbi_hfence_gvma_vmid_gpa:
/*
* rs1 = a0 (GPA)
* rs1 = a0 (GPA >> 2)
* rs2 = a1 (VMID)
* HFENCE.GVMA a0, a1
* 0110001 01011 01010 000 00000 1110011
@@ -51,7 +51,7 @@ __sbi_hfence_gvma_vmid:
.global __sbi_hfence_gvma_gpa
__sbi_hfence_gvma_gpa:
/*
* rs1 = a0 (GPA)
* rs1 = a0 (GPA >> 2)
* rs2 = zero
* HFENCE.GVMA a0
* 0110001 00000 01010 000 00000 1110011

View File

@@ -185,8 +185,7 @@ int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
struct sbi_hsm_data *hdata;
if (cold_boot) {
hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata),
"HART_DATA");
hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata));
if (!hart_data_offset)
return SBI_ENOMEM;
@@ -359,12 +358,6 @@ static int __sbi_hsm_suspend_non_ret_default(struct sbi_scratch *scratch,
{
void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
/*
* Save some of the M-mode CSRs which should be restored after
* resuming from suspend state
*/
__sbi_hsm_suspend_non_ret_save(scratch);
/* Wait for interrupt */
wfi();
@@ -464,6 +457,13 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
/* Save the suspend type */
hdata->suspend_type = suspend_type;
/*
* Save context which will be restored after resuming from
* non-retentive suspend.
*/
if (suspend_type & SBI_HSM_SUSP_NON_RET_BIT)
__sbi_hsm_suspend_non_ret_save(scratch);
/* Try platform specific suspend */
ret = hsm_device_hart_suspend(suspend_type, scratch->warmboot_addr);
if (ret == SBI_ENOTSUPP) {

View File

@@ -13,6 +13,7 @@
#include <sbi/sbi_emulate_csr.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_illegal_insn.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_trap.h>
#include <sbi/sbi_unpriv.h>
@@ -129,6 +130,7 @@ int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
* instruction trap.
*/
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_ILLEGAL_INSN);
if (unlikely((insn & 3) != 3)) {
insn = sbi_get_insn(regs->mepc, &uptrap);
if (uptrap.cause) {

View File

@@ -19,6 +19,7 @@
#include <sbi/sbi_hsm.h>
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_system.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_timer.h>
@@ -47,6 +48,14 @@ static void sbi_boot_print_banner(struct sbi_scratch *scratch)
OPENSBI_VERSION_MINOR);
#endif
#ifdef OPENSBI_BUILD_TIME_STAMP
sbi_printf("Build time: %s\n", OPENSBI_BUILD_TIME_STAMP);
#endif
#ifdef OPENSBI_BUILD_COMPILER_VERSION
sbi_printf("Build compiler: %s\n", OPENSBI_BUILD_COMPILER_VERSION);
#endif
sbi_printf(BANNER);
}
@@ -74,16 +83,20 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
sbi_printf("Platform IPI Device : %s\n",
(idev) ? idev->name : "---");
tdev = sbi_timer_get_device();
sbi_printf("Platform Timer Device : %s\n",
(tdev) ? tdev->name : "---");
sbi_printf("Platform Timer Device : %s @ %luHz\n",
(tdev) ? tdev->name : "---",
(tdev) ? tdev->timer_freq : 0);
cdev = sbi_console_get_device();
sbi_printf("Platform Console Device : %s\n",
(cdev) ? cdev->name : "---");
hdev = sbi_hsm_get_device();
sbi_printf("Platform HSM Device : %s\n",
(hdev) ? hdev->name : "---");
srdev = sbi_system_reset_get_device();
sbi_printf("Platform SysReset Device : %s\n",
srdev = sbi_system_reset_get_device(SBI_SRST_RESET_TYPE_COLD_REBOOT, 0);
sbi_printf("Platform Reboot Device : %s\n",
(srdev) ? srdev->name : "---");
srdev = sbi_system_reset_get_device(SBI_SRST_RESET_TYPE_SHUTDOWN, 0);
sbi_printf("Platform Shutdown Device : %s\n",
(srdev) ? srdev->name : "---");
/* Firmware details */
@@ -137,8 +150,6 @@ static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_pmp_addrbits(scratch));
sbi_printf("Boot HART MHPM Count : %d\n",
sbi_hart_mhpm_count(scratch));
sbi_printf("Boot HART MHPM Count : %d\n",
sbi_hart_mhpm_count(scratch));
sbi_hart_delegation_dump(scratch, "Boot HART ", " ");
}
@@ -205,7 +216,7 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid)
spin_lock(&coldboot_lock);
/* Send an IPI to all HARTs waiting for coldboot */
for (int i = 0; i <= sbi_scratch_last_hartid(); i++) {
for (u32 i = 0; i <= sbi_scratch_last_hartid(); i++) {
if ((i != hartid) &&
sbi_hartmask_test_hart(i, &coldboot_wait_hmask))
sbi_ipi_raw_send(i);
@@ -233,8 +244,7 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
if (rc)
sbi_hart_hang();
init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__,
"INIT_COUNT");
init_count_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__);
if (!init_count_offset)
sbi_hart_hang();
@@ -254,6 +264,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
if (rc)
sbi_hart_hang();
rc = sbi_pmu_init(scratch, TRUE);
if (rc)
sbi_hart_hang();
sbi_boot_print_banner(scratch);
rc = sbi_platform_irqchip_init(plat, TRUE);
@@ -287,8 +301,6 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_hang();
}
sbi_boot_print_general(scratch);
/*
* Note: Finalize domains after HSM initialization so that we
* can startup non-root domains.
@@ -302,8 +314,6 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_hang();
}
sbi_boot_print_domains(scratch);
rc = sbi_hart_pmp_configure(scratch);
if (rc) {
sbi_printf("%s: PMP configure failed (error %d)\n",
@@ -322,6 +332,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_hang();
}
sbi_boot_print_general(scratch);
sbi_boot_print_domains(scratch);
sbi_boot_print_hart(scratch, hartid);
wake_coldboot_harts(scratch, hartid);
@@ -355,6 +369,10 @@ static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid)
if (rc)
sbi_hart_hang();
rc = sbi_pmu_init(scratch, FALSE);
if (rc)
sbi_hart_hang();
rc = sbi_platform_irqchip_init(plat, FALSE);
if (rc)
sbi_hart_hang();
@@ -518,6 +536,8 @@ void __noreturn sbi_exit(struct sbi_scratch *scratch)
sbi_platform_early_exit(plat);
sbi_pmu_exit(scratch);
sbi_timer_exit(scratch);
sbi_ipi_exit(scratch);

View File

@@ -19,6 +19,9 @@
#include <sbi/sbi_init.h>
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_tlb.h>
struct sbi_ipi_data {
unsigned long ipi_type;
@@ -64,6 +67,8 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
if (ipi_dev && ipi_dev->ipi_send)
ipi_dev->ipi_send(remote_hartid);
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_IPI_SENT);
if (ipi_ops->sync)
ipi_ops->sync(scratch);
@@ -183,6 +188,7 @@ void sbi_ipi_process(void)
sbi_scratch_offset_ptr(scratch, ipi_data_off);
u32 hartid = current_hartid();
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_IPI_RECVD);
if (ipi_dev && ipi_dev->ipi_clear)
ipi_dev->ipi_clear(hartid);
@@ -227,8 +233,7 @@ int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot)
struct sbi_ipi_data *ipi_data;
if (cold_boot) {
ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data),
"IPI_DATA");
ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data));
if (!ipi_data_off)
return SBI_ENOMEM;
ret = sbi_ipi_event_create(&ipi_smode_ops);

View File

@@ -12,6 +12,7 @@
#include <sbi/riscv_fp.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_misaligned_ldst.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_trap.h>
#include <sbi/sbi_unpriv.h>
@@ -29,6 +30,8 @@ int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst,
struct sbi_trap_info uptrap;
int i, fp = 0, shift = 0, len = 0;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_MISALIGNED_LOAD);
if (tinst & 0x1) {
/*
* Bit[0] == 1 implies trapped instruction value is
@@ -149,6 +152,8 @@ int sbi_misaligned_store_handler(ulong addr, ulong tval2, ulong tinst,
struct sbi_trap_info uptrap;
int i, len = 0;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_MISALIGNED_STORE);
if (tinst & 0x1) {
/*
* Bit[0] == 1 implies trapped instruction value is

View File

@@ -20,7 +20,7 @@ static inline char *sbi_platform_feature_id2string(unsigned long feature)
switch (feature) {
case SBI_PLATFORM_HAS_MFAULTS_DELEGATION:
fstr = "mfdeleg";
fstr = "medeleg";
break;
default:
break;
@@ -48,9 +48,18 @@ void sbi_platform_get_features_str(const struct sbi_platform *plat,
if (features & feat) {
temp = sbi_platform_feature_id2string(feat);
if (temp) {
sbi_snprintf(features_str + offset, nfstr,
"%s,", temp);
offset = offset + sbi_strlen(temp) + 1;
int len = sbi_snprintf(features_str + offset,
nfstr - offset,
"%s,", temp);
if (len < 0)
break;
if (offset + len >= nfstr) {
/* No more space for features */
offset = nfstr;
break;
} else
offset = offset + len;
}
}
feat = feat << 1;

753
lib/sbi/sbi_pmu.c Normal file
View File

@@ -0,0 +1,753 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <sbi/riscv_asm.h>
#include <sbi/sbi_bitops.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_string.h>
/** Information about hardware counters */
struct sbi_pmu_hw_event {
uint32_t counters;
uint32_t start_idx;
uint32_t end_idx;
/* Event selector value used only for raw events. The event select value
* can be a even id or a selector value for set of events encoded in few
* bits. In case latter, the bits used for encoding of the events should
* be zeroed out in the select value.
*/
uint64_t select;
/**
* The select_mask indicates which bits are encoded for the event(s).
*/
uint64_t select_mask;
};
/** Representation of a firmware event */
struct sbi_pmu_fw_event {
/* Event associated with the particular counter */
uint32_t event_idx;
/* Current value of the counter */
unsigned long curr_count;
/* A flag indicating pmu event monitoring is started */
bool bStarted;
};
/* Information about PMU counters as per SBI specification */
union sbi_pmu_ctr_info {
unsigned long value;
struct {
unsigned long csr:12;
unsigned long width:6;
#if __riscv_xlen == 32
unsigned long reserved:13;
#else
unsigned long reserved:45;
#endif
unsigned long type:1;
};
};
/* Mapping between event range and possible counters */
static struct sbi_pmu_hw_event hw_event_map[SBI_PMU_HW_EVENT_MAX] = {0};
/* counter to enabled event mapping */
static uint32_t active_events[SBI_HARTMASK_MAX_BITS][SBI_PMU_HW_CTR_MAX + SBI_PMU_FW_CTR_MAX];
/* Contains all the information about firmwares events */
static struct sbi_pmu_fw_event fw_event_map[SBI_HARTMASK_MAX_BITS][SBI_PMU_FW_EVENT_MAX] = {0};
/* Maximum number of hardware events available */
static uint32_t num_hw_events;
/* Maximum number of hardware counters available */
static uint32_t num_hw_ctrs;
/* Maximum number of counters available */
static uint32_t total_ctrs;
/* Helper macros to retrieve event idx and code type */
#define get_cidx_type(x) ((x & SBI_PMU_EVENT_IDX_TYPE_MASK) >> 16)
#define get_cidx_code(x) (x & SBI_PMU_EVENT_IDX_CODE_MASK)
/**
* Perform a sanity check on event & counter mappings with event range overlap check
* @param evtA Pointer to the existing hw event structure
* @param evtB Pointer to the new hw event structure
*
* Return FALSE if the range doesn't overlap, TRUE otherwise
*/
static bool pmu_event_range_overlap(struct sbi_pmu_hw_event *evtA,
struct sbi_pmu_hw_event *evtB)
{
/* check if the range of events overlap with a previous entry */
if (((evtA->end_idx < evtB->start_idx) && (evtA->end_idx < evtB->end_idx)) ||
((evtA->start_idx > evtB->start_idx) && (evtA->start_idx > evtB->end_idx)))
return FALSE;
return TRUE;
}
static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
uint64_t select_val, uint64_t select_mask)
{
if ((evt->select == select_val) && (evt->select_mask == select_mask))
return TRUE;
return FALSE;
}
static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
{
uint32_t event_idx_val;
uint32_t event_idx_type;
u32 hartid = current_hartid();
event_idx_val = active_events[hartid][cidx];
if (cidx >= total_ctrs || (event_idx_val == SBI_PMU_EVENT_IDX_INVALID))
return SBI_EINVAL;
event_idx_type = get_cidx_type(event_idx_val);
if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
return SBI_EINVAL;
*event_idx_code = get_cidx_code(event_idx_val);
return event_idx_type;
}
static int pmu_ctr_read_fw(uint32_t cidx, unsigned long *cval,
uint32_t fw_evt_code)
{
u32 hartid = current_hartid();
struct sbi_pmu_fw_event fevent;
fevent = fw_event_map[hartid][fw_evt_code];
*cval = fevent.curr_count;
return 0;
}
/* Add a hardware counter read for completeness for future purpose */
static int pmu_ctr_read_hw(uint32_t cidx, uint64_t *cval)
{
/* Check for invalid hw counter read requests */
if (unlikely(cidx == 1))
return SBI_EINVAL;
#if __riscv_xlen == 32
uint32_t temp, temph = 0;
temp = csr_read_num(CSR_MCYCLE + cidx);
temph = csr_read_num(CSR_MCYCLEH + cidx);
*cval = ((uint64_t)temph << 32) | temp;
#else
*cval = csr_read_num(CSR_MCYCLE + cidx);
#endif
return 0;
}
int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval)
{
int event_idx_type;
uint32_t event_code;
uint64_t cval64;
event_idx_type = pmu_ctr_validate(cidx, &event_code);
if (event_idx_type < 0)
return SBI_EINVAL;
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
pmu_ctr_read_fw(cidx, cval, event_code);
else
pmu_ctr_read_hw(cidx, &cval64);
return 0;
}
static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap,
uint64_t select, uint64_t select_mask)
{
int i = 0;
bool is_overlap;
struct sbi_pmu_hw_event *event = &hw_event_map[num_hw_events];
/* The first two counters are reserved by priv spec */
if (eidx_start > SBI_PMU_HW_INSTRUCTIONS && (cmap & SBI_PMU_FIXED_CTR_MASK))
return SBI_EDENIED;
if (num_hw_events >= SBI_PMU_HW_EVENT_MAX - 1) {
sbi_printf("Can not handle more than %d perf events\n",
SBI_PMU_HW_EVENT_MAX);
return SBI_EFAIL;
}
event->start_idx = eidx_start;
event->end_idx = eidx_end;
/* Sanity check */
for (i = 0; i < num_hw_events; i++) {
if (eidx_start == SBI_PMU_EVENT_RAW_IDX)
/* All raw events have same event idx. Just do sanity check on select */
is_overlap = pmu_event_select_overlap(&hw_event_map[i],
select, select_mask);
else
is_overlap = pmu_event_range_overlap(&hw_event_map[i], event);
if (is_overlap)
goto reset_event;
}
event->select_mask = select_mask;
event->counters = cmap;
event->select = select;
num_hw_events++;
return 0;
reset_event:
event->start_idx = 0;
event->end_idx = 0;
return SBI_EINVAL;
}
/**
* Logical counter ids are assigned to hardware counters are assigned consecutively.
* E.g. counter0 must count MCYCLE where counter2 must count minstret. Similarly,
* counterX will mhpmcounterX.
*/
int sbi_pmu_add_hw_event_counter_map(u32 eidx_start, u32 eidx_end, u32 cmap)
{
if ((eidx_start > eidx_end) || eidx_start == SBI_PMU_EVENT_RAW_IDX ||
eidx_end == SBI_PMU_EVENT_RAW_IDX)
return SBI_EINVAL;
return pmu_add_hw_event_map(eidx_start, eidx_end, cmap, 0, 0);
}
int sbi_pmu_add_raw_event_counter_map(uint64_t select, uint64_t select_mask, u32 cmap)
{
return pmu_add_hw_event_map(SBI_PMU_EVENT_RAW_IDX,
SBI_PMU_EVENT_RAW_IDX, cmap, select, select_mask);
}
static int pmu_ctr_enable_irq_hw(int ctr_idx)
{
unsigned long mhpmevent_csr;
unsigned long mhpmevent_curr;
unsigned long mip_val;
unsigned long of_mask;
if (ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
return SBI_EFAIL;
#if __riscv_xlen == 32
mhpmevent_csr = CSR_MHPMEVENT3H + ctr_idx - 3;
of_mask = ~MHPMEVENTH_OF;
#else
mhpmevent_csr = CSR_MHPMEVENT3 + ctr_idx - 3;
of_mask = ~MHPMEVENT_OF;
#endif
mhpmevent_curr = csr_read_num(mhpmevent_csr);
mip_val = csr_read(CSR_MIP);
/**
* Clear out the OF bit so that next interrupt can be enabled.
* This should be done only when the corresponding overflow interrupt
* bit is cleared. That indicates that software has already handled the
* previous interrupts or the hardware yet to set an overflow interrupt.
* Otherwise, there will be race conditions where we may clear the bit
* the software is yet to handle the interrupt.
*/
if (!(mip_val & MIP_LCOFIP)) {
mhpmevent_curr &= of_mask;
csr_write_num(mhpmevent_csr, mhpmevent_curr);
}
return 0;
}
static void pmu_ctr_write_hw(uint32_t cidx, uint64_t ival)
{
#if __riscv_xlen == 32
csr_write_num(CSR_MCYCLE + cidx, 0);
csr_write_num(CSR_MCYCLE + cidx, ival & 0xFFFFFFFF);
csr_write_num(CSR_MCYCLEH + cidx, ival >> BITS_PER_LONG);
#else
csr_write_num(CSR_MCYCLE + cidx, ival);
#endif
}
static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool ival_update)
{
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
unsigned long mctr_inhbt;
/* Make sure the counter index lies within the range and is not TM bit */
if (cidx > num_hw_ctrs || cidx == 1)
return SBI_EINVAL;
if (!sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
goto skip_inhibit_update;
/*
* Some of the hardware may not support mcountinhibit but perf stat
* still can work if supervisor mode programs the initial value.
*/
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
if (!__test_bit(cidx, &mctr_inhbt))
return SBI_EALREADY_STARTED;
__clear_bit(cidx, &mctr_inhbt);
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
pmu_ctr_enable_irq_hw(cidx);
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
skip_inhibit_update:
if (ival_update)
pmu_ctr_write_hw(cidx, ival);
return 0;
}
static int pmu_ctr_start_fw(uint32_t cidx, uint32_t fw_evt_code,
uint64_t ival, bool ival_update)
{
u32 hartid = current_hartid();
struct sbi_pmu_fw_event *fevent;
fevent = &fw_event_map[hartid][fw_evt_code];
if (ival_update)
fevent->curr_count = ival;
fevent->bStarted = TRUE;
return 0;
}
int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
unsigned long flags, uint64_t ival)
{
int event_idx_type;
uint32_t event_code;
unsigned long ctr_mask = cmask << cbase;
int ret = SBI_EINVAL;
bool bUpdate = FALSE;
if (__fls(ctr_mask) >= total_ctrs)
return ret;
if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
bUpdate = TRUE;
for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
event_idx_type = pmu_ctr_validate(cbase, &event_code);
if (event_idx_type < 0)
/* Continue the start operation for other counters */
continue;
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
else
ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
}
return ret;
}
static int pmu_ctr_stop_hw(uint32_t cidx)
{
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
unsigned long mctr_inhbt;
if (!sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
return 0;
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
/* Make sure the counter index lies within the range and is not TM bit */
if (cidx > num_hw_ctrs || cidx == 1)
return SBI_EINVAL;
if (!__test_bit(cidx, &mctr_inhbt)) {
__set_bit(cidx, &mctr_inhbt);
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
return 0;
} else
return SBI_EALREADY_STOPPED;
}
static int pmu_ctr_stop_fw(uint32_t cidx, uint32_t fw_evt_code)
{
u32 hartid = current_hartid();
fw_event_map[hartid][fw_evt_code].bStarted = FALSE;
return 0;
}
static int pmu_reset_hw_mhpmevent(int ctr_idx)
{
if (ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
return SBI_EFAIL;
#if __riscv_xlen == 32
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, 0);
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3, 0);
#else
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, 0);
#endif
return 0;
}
int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
unsigned long flag)
{
u32 hartid = current_hartid();
int ret = SBI_EINVAL;
int event_idx_type;
uint32_t event_code;
unsigned long ctr_mask = cmask << cbase;
if (__fls(ctr_mask) >= total_ctrs)
return SBI_EINVAL;
for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
event_idx_type = pmu_ctr_validate(cbase, &event_code);
if (event_idx_type < 0)
/* Continue the stop operation for other counters */
continue;
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
ret = pmu_ctr_stop_fw(cbase, event_code);
else
ret = pmu_ctr_stop_hw(cbase);
if (flag & SBI_PMU_STOP_FLAG_RESET) {
active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
pmu_reset_hw_mhpmevent(cbase);
}
}
return ret;
}
static void pmu_update_inhibit_flags(unsigned long flags, uint64_t *mhpmevent_val)
{
if (flags & SBI_PMU_CFG_FLAG_SET_VUINH)
*mhpmevent_val |= MHPMEVENT_VUINH;
if (flags & SBI_PMU_CFG_FLAG_SET_VSINH)
*mhpmevent_val |= MHPMEVENT_VSINH;
if (flags & SBI_PMU_CFG_FLAG_SET_UINH)
*mhpmevent_val |= MHPMEVENT_UINH;
if (flags & SBI_PMU_CFG_FLAG_SET_SINH)
*mhpmevent_val |= MHPMEVENT_SINH;
}
static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event *hw_evt, int ctr_idx,
unsigned long flags, unsigned long eindex,
uint64_t data)
{
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
uint64_t mhpmevent_val;
/* Get the final mhpmevent value to be written from platform */
mhpmevent_val = sbi_platform_pmu_xlate_to_mhpmevent(plat, eindex, data);
if (!mhpmevent_val || ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
return SBI_EFAIL;
/* Always clear the OVF bit and inhibit countin of events in M-mode */
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) | MHPMEVENT_MINH;
/* Update the inhibit flags based on inhibit flags received from supervisor */
pmu_update_inhibit_flags(flags, &mhpmevent_val);
#if __riscv_xlen == 32
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, mhpmevent_val & 0xFFFFFFFF);
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3, mhpmevent_val >> BITS_PER_LONG);
#else
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, mhpmevent_val);
#endif
return 0;
}
static int pmu_ctr_find_fixed_fw(unsigned long evt_idx_code)
{
/* Non-programmables counters are enabled always. No need to do lookup */
if (evt_idx_code == SBI_PMU_HW_CPU_CYCLES)
return 0;
else if (evt_idx_code == SBI_PMU_HW_INSTRUCTIONS)
return 2;
else
return SBI_EINVAL;
}
static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned long flags,
unsigned long event_idx, uint64_t data)
{
unsigned long ctr_mask;
int i, ret = 0, fixed_ctr, ctr_idx = SBI_ENOTSUPP;
struct sbi_pmu_hw_event *temp;
unsigned long mctr_inhbt = 0;
u32 hartid = current_hartid();
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
if (cbase > num_hw_ctrs)
return SBI_EINVAL;
/**
* If Sscof is present try to find the programmable counter for
* cycle/instret as well.
*/
fixed_ctr = pmu_ctr_find_fixed_fw(event_idx);
if (fixed_ctr >= 0 &&
!sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
return fixed_ctr;
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
for (i = 0; i < num_hw_events; i++) {
temp = &hw_event_map[i];
if ((temp->start_idx > event_idx && event_idx < temp->end_idx) ||
(temp->start_idx < event_idx && event_idx > temp->end_idx))
continue;
/* For raw events, event data is used as the select value */
if (event_idx == SBI_PMU_EVENT_RAW_IDX) {
uint64_t select_mask = temp->select_mask;
/* The non-event map bits of data should match the selector */
if (temp->select != (data & select_mask))
continue;
}
/* Fixed counters should not be part of the search */
ctr_mask = temp->counters & (cmask << cbase) &
(~SBI_PMU_FIXED_CTR_MASK);
for_each_set_bit_from(cbase, &ctr_mask, SBI_PMU_HW_CTR_MAX) {
/**
* Some of the platform may not support mcountinhibit.
* Checking the active_events is enough for them
*/
if (active_events[hartid][cbase] != SBI_PMU_EVENT_IDX_INVALID)
continue;
/* If mcountinhibit is supported, the bit must be enabled */
if ((sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT)) &&
!__test_bit(cbase, &mctr_inhbt))
continue;
/* We found a valid counter that is not started yet */
ctr_idx = cbase;
}
}
if (ctr_idx == SBI_ENOTSUPP) {
/**
* We can't find any programmable counters for cycle/instret.
* Return the fixed counter as they are mandatory anyways.
*/
if (fixed_ctr >= 0)
return fixed_ctr;
else
return SBI_EFAIL;
}
ret = pmu_update_hw_mhpmevent(temp, ctr_idx, flags, event_idx, data);
if (!ret)
ret = ctr_idx;
return ret;
}
/**
* Any firmware counter can map to any firmware event.
* Thus, select the first available fw counter after sanity
* check.
*/
static int pmu_ctr_find_fw(unsigned long cbase, unsigned long cmask, u32 hartid)
{
int i = 0;
int fw_base;
unsigned long ctr_mask = cmask << cbase;
if (cbase <= num_hw_ctrs)
fw_base = num_hw_ctrs + 1;
else
fw_base = cbase;
for (i = fw_base; i < total_ctrs; i++)
if ((active_events[hartid][i] == SBI_PMU_EVENT_IDX_INVALID) &&
((1UL << i) & ctr_mask))
return i;
return SBI_ENOTSUPP;
}
int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
unsigned long flags, unsigned long event_idx,
uint64_t event_data)
{
int ctr_idx = SBI_ENOTSUPP;
u32 hartid = current_hartid();
int event_type = get_cidx_type(event_idx);
struct sbi_pmu_fw_event *fevent;
uint32_t fw_evt_code;
unsigned long tmp = cidx_mask << cidx_base;
/* Do a basic sanity check of counter base & mask */
if (__fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
return SBI_EINVAL;
if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
/* The caller wants to skip the match because it already knows the
* counter idx for the given event. Verify that the counter idx
* is still valid.
*/
if (active_events[hartid][cidx_base] == SBI_PMU_EVENT_IDX_INVALID)
return SBI_EINVAL;
ctr_idx = cidx_base;
goto skip_match;
}
if (event_type == SBI_PMU_EVENT_TYPE_FW) {
/* Any firmware counter can be used track any firmware event */
ctr_idx = pmu_ctr_find_fw(cidx_base, cidx_mask, hartid);
} else {
ctr_idx = pmu_ctr_find_hw(cidx_base, cidx_mask, flags, event_idx,
event_data);
}
if (ctr_idx < 0)
return SBI_ENOTSUPP;
active_events[hartid][ctr_idx] = event_idx;
skip_match:
if (event_type == SBI_PMU_EVENT_TYPE_HW) {
if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
pmu_ctr_write_hw(ctr_idx, 0);
if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
pmu_ctr_start_hw(ctr_idx, 0, false);
} else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
fw_evt_code = get_cidx_code(event_idx);
fevent = &fw_event_map[hartid][fw_evt_code];
if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
fevent->curr_count = 0;
if (flags & SBI_PMU_CFG_FLAG_AUTO_START)
fevent->bStarted = TRUE;
}
return ctr_idx;
}
inline int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id)
{
u32 hartid = current_hartid();
struct sbi_pmu_fw_event *fevent;
if (unlikely(fw_id >= SBI_PMU_FW_MAX))
return SBI_EINVAL;
fevent = &fw_event_map[hartid][fw_id];
/* PMU counters will be only enabled during performance debugging */
if (unlikely(fevent->bStarted))
fevent->curr_count++;
return 0;
}
unsigned long sbi_pmu_num_ctr(void)
{
return (num_hw_ctrs + SBI_PMU_FW_CTR_MAX);
}
int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info)
{
union sbi_pmu_ctr_info cinfo = {0};
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
/* Sanity check. Counter1 is not mapped at all */
if (cidx >= total_ctrs || cidx == 1)
return SBI_EINVAL;
/* We have 31 HW counters with 31 being the last index(MHPMCOUNTER31) */
if (cidx <= num_hw_ctrs) {
cinfo.type = SBI_PMU_CTR_TYPE_HW;
cinfo.csr = CSR_CYCLE + cidx;
/* mcycle & minstret are always 64 bit */
if (cidx == 0 || cidx == 2)
cinfo.width = 63;
else
cinfo.width = sbi_hart_mhpm_bits(scratch) - 1;
} else {
/* it's a firmware counter */
cinfo.type = SBI_PMU_CTR_TYPE_FW;
/* Firmware counters are XLEN bits wide */
cinfo.width = BITS_PER_LONG - 1;
}
*ctr_info = cinfo.value;
return 0;
}
static void pmu_reset_event_map(u32 hartid)
{
int j;
/* Initialize the counter to event mapping table */
for (j = 3; j < total_ctrs; j++)
active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
sbi_memset(&fw_event_map[hartid][j], 0,
sizeof(struct sbi_pmu_fw_event));
}
void sbi_pmu_exit(struct sbi_scratch *scratch)
{
u32 hartid = current_hartid();
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
csr_write(CSR_MCOUNTEREN, -1);
pmu_reset_event_map(hartid);
}
int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot)
{
const struct sbi_platform *plat;
u32 hartid = current_hartid();
if (cold_boot) {
plat = sbi_platform_ptr(scratch);
/* Initialize hw pmu events */
sbi_platform_pmu_init(plat);
/* mcycle & minstret is available always */
num_hw_ctrs = sbi_hart_mhpm_count(scratch) + 2;
total_ctrs = num_hw_ctrs + SBI_PMU_FW_CTR_MAX;
}
pmu_reset_event_map(hartid);
/* First three counters are fixed by the priv spec and we enable it by default */
active_events[hartid][0] = SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_OFFSET |
SBI_PMU_HW_CPU_CYCLES;
active_events[hartid][1] = SBI_PMU_EVENT_IDX_INVALID;
active_events[hartid][2] = SBI_PMU_EVENT_TYPE_HW << SBI_PMU_EVENT_IDX_OFFSET |
SBI_PMU_HW_INSTRUCTIONS;
return 0;
}

View File

@@ -14,7 +14,7 @@
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_string.h>
u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS;
u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS - 1;
struct sbi_scratch *hartid_to_scratch_table[SBI_HARTMASK_MAX_BITS] = { 0 };
static spinlock_t extra_lock = SPIN_LOCK_INITIALIZER;
@@ -40,7 +40,7 @@ int sbi_scratch_init(struct sbi_scratch *scratch)
return 0;
}
unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner)
unsigned long sbi_scratch_alloc_offset(unsigned long size)
{
u32 i;
void *ptr;
@@ -74,7 +74,7 @@ done:
spin_unlock(&extra_lock);
if (ret) {
for (i = 0; i < sbi_scratch_last_hartid(); i++) {
for (i = 0; i <= sbi_scratch_last_hartid(); i++) {
rscratch = sbi_hartid_to_scratch(i);
if (!rscratch)
continue;

View File

@@ -33,6 +33,10 @@ int sbi_strncmp(const char *a, const char *b, size_t count)
for (; count > 0 && *a == *b && *a != '\0'; a++, b++, count--)
;
/* No difference till the end */
if (!count)
return 0;
return *a - *b;
}

View File

@@ -18,28 +18,48 @@
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_init.h>
static const struct sbi_system_reset_device *reset_dev = NULL;
static SBI_LIST_HEAD(reset_devices_list);
const struct sbi_system_reset_device *sbi_system_reset_get_device(void)
const struct sbi_system_reset_device *sbi_system_reset_get_device(
u32 reset_type, u32 reset_reason)
{
struct sbi_system_reset_device *reset_dev = NULL;
struct sbi_dlist *pos;
/** lowest priority - any non zero is our candidate */
int priority = 0;
/* Check each reset device registered for supported reset type */
sbi_list_for_each(pos, &(reset_devices_list)) {
struct sbi_system_reset_device *dev =
to_system_reset_device(pos);
if (dev->system_reset_check) {
int status = dev->system_reset_check(reset_type,
reset_reason);
/** reset_type not supported */
if (status == 0)
continue;
if (status > priority) {
reset_dev = dev;
priority = status;
}
}
}
return reset_dev;
}
void sbi_system_reset_set_device(const struct sbi_system_reset_device *dev)
void sbi_system_reset_add_device(struct sbi_system_reset_device *dev)
{
if (!dev || reset_dev)
if (!dev || !dev->system_reset_check)
return;
reset_dev = dev;
sbi_list_add(&(dev->node), &(reset_devices_list));
}
bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason)
{
if (reset_dev && reset_dev->system_reset_check &&
reset_dev->system_reset_check(reset_type, reset_reason))
return TRUE;
return FALSE;
return !!sbi_system_reset_get_device(reset_type, reset_reason);
}
void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
@@ -62,9 +82,12 @@ void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
sbi_hsm_hart_stop(scratch, FALSE);
/* Platform specific reset if domain allowed system reset */
if (dom->system_reset_allowed &&
reset_dev && reset_dev->system_reset)
reset_dev->system_reset(reset_type, reset_reason);
if (dom->system_reset_allowed) {
const struct sbi_system_reset_device *dev =
sbi_system_reset_get_device(reset_type, reset_reason);
if (dev)
dev->system_reset(reset_type, reset_reason);
}
/* If platform specific reset did not work then do sbi_exit() */
sbi_exit(scratch);

View File

@@ -8,10 +8,13 @@
*/
#include <sbi/riscv_asm.h>
#include <sbi/riscv_barrier.h>
#include <sbi/riscv_encoding.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_timer.h>
@@ -46,6 +49,38 @@ static u64 get_platform_ticks(void)
return timer_dev->timer_value();
}
static void nop_delay_fn(void *opaque)
{
cpu_relax();
}
void sbi_timer_delay_loop(ulong units, u64 unit_freq,
void (*delay_fn)(void *), void *opaque)
{
u64 start_val, delta;
/* Do nothing if we don't have timer device */
if (!timer_dev || !get_time_val) {
sbi_printf("%s: called without timer device\n", __func__);
return;
}
/* Save starting timer value */
start_val = get_time_val();
/* Compute desired timer value delta */
delta = ((u64)timer_dev->timer_freq * (u64)units);
delta = delta / unit_freq;
/* Use NOP delay function if delay function not available */
if (!delay_fn)
delay_fn = nop_delay_fn;
/* Busy loop until desired timer value delta reached */
while ((get_time_val() - start_val) < delta)
delay_fn(opaque);
}
u64 sbi_timer_value(void)
{
if (get_time_val)
@@ -88,6 +123,7 @@ void sbi_timer_set_delta_upper(ulong delta_upper)
void sbi_timer_event_start(u64 next_event)
{
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SET_TIMER);
if (timer_dev && timer_dev->timer_event_start)
timer_dev->timer_event_start(next_event);
csr_clear(CSR_MIP, MIP_STIP);
@@ -121,8 +157,7 @@ int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
if (cold_boot) {
time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta),
"TIME_DELTA");
time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta));
if (!time_delta_off)
return SBI_ENOMEM;

View File

@@ -21,13 +21,14 @@
#include <sbi/sbi_string.h>
#include <sbi/sbi_console.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_pmu.h>
static unsigned long tlb_sync_off;
static unsigned long tlb_fifo_off;
static unsigned long tlb_fifo_mem_off;
static unsigned long tlb_range_flush_limit;
static void sbi_tlb_flush_all(void)
static void tlb_flush_all(void)
{
__asm__ __volatile("sfence.vma");
}
@@ -39,6 +40,8 @@ void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo)
unsigned long vmid = tinfo->vmid;
unsigned long i, hgatp;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_RCVD);
hgatp = csr_swap(CSR_HGATP,
(vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK);
@@ -61,13 +64,15 @@ void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo)
unsigned long size = tinfo->size;
unsigned long i;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_RCVD);
if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
__sbi_hfence_gvma_all();
return;
}
for (i = 0; i < size; i += PAGE_SIZE) {
__sbi_hfence_gvma_gpa(start+i);
__sbi_hfence_gvma_gpa((start + i) >> 2);
}
}
@@ -77,8 +82,10 @@ void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo)
unsigned long size = tinfo->size;
unsigned long i;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD);
if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
sbi_tlb_flush_all();
tlb_flush_all();
return;
}
@@ -98,6 +105,8 @@ void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo)
unsigned long vmid = tinfo->vmid;
unsigned long i, hgatp;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD);
hgatp = csr_swap(CSR_HGATP,
(vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK);
@@ -126,6 +135,8 @@ void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo)
unsigned long vmid = tinfo->vmid;
unsigned long i;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD);
if (start == 0 && size == 0) {
__sbi_hfence_gvma_all();
return;
@@ -137,7 +148,7 @@ void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo)
}
for (i = 0; i < size; i += PAGE_SIZE) {
__sbi_hfence_gvma_vmid_gpa(start + i, vmid);
__sbi_hfence_gvma_vmid_gpa((start + i) >> 2, vmid);
}
}
@@ -148,8 +159,10 @@ void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
unsigned long asid = tinfo->asid;
unsigned long i;
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD);
if (start == 0 && size == 0) {
sbi_tlb_flush_all();
tlb_flush_all();
return;
}
@@ -172,10 +185,33 @@ void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo)
{
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_RECVD);
__asm__ __volatile("fence.i");
}
static void sbi_tlb_entry_process(struct sbi_tlb_info *tinfo)
static void tlb_pmu_incr_fw_ctr(struct sbi_tlb_info *data)
{
if (unlikely(!data))
return;
if (data->local_fn == sbi_tlb_local_fence_i)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_SENT);
else if (data->local_fn == sbi_tlb_local_sfence_vma)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_SENT);
else if (data->local_fn == sbi_tlb_local_sfence_vma_asid)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_SENT);
else if (data->local_fn == sbi_tlb_local_hfence_gvma)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_SENT);
else if (data->local_fn == sbi_tlb_local_hfence_gvma_vmid)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_SENT);
else if (data->local_fn == sbi_tlb_local_hfence_vvma)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_SENT);
else if (data->local_fn == sbi_tlb_local_hfence_vvma_asid)
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_ASID_SENT);
}
static void tlb_entry_process(struct sbi_tlb_info *tinfo)
{
u32 rhartid;
struct sbi_scratch *rscratch = NULL;
@@ -193,15 +229,15 @@ static void sbi_tlb_entry_process(struct sbi_tlb_info *tinfo)
}
}
static void sbi_tlb_process_count(struct sbi_scratch *scratch, int count)
static void tlb_process_count(struct sbi_scratch *scratch, int count)
{
struct sbi_tlb_info tinfo;
u32 deq_count = 0;
unsigned int deq_count = 0;
struct sbi_fifo *tlb_fifo =
sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
while (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) {
sbi_tlb_entry_process(&tinfo);
tlb_entry_process(&tinfo);
deq_count++;
if (deq_count > count)
break;
@@ -209,17 +245,17 @@ static void sbi_tlb_process_count(struct sbi_scratch *scratch, int count)
}
}
static void sbi_tlb_process(struct sbi_scratch *scratch)
static void tlb_process(struct sbi_scratch *scratch)
{
struct sbi_tlb_info tinfo;
struct sbi_fifo *tlb_fifo =
sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
while (!sbi_fifo_dequeue(tlb_fifo, &tinfo))
sbi_tlb_entry_process(&tinfo);
tlb_entry_process(&tinfo);
}
static void sbi_tlb_sync(struct sbi_scratch *scratch)
static void tlb_sync(struct sbi_scratch *scratch)
{
unsigned long *tlb_sync =
sbi_scratch_offset_ptr(scratch, tlb_sync_off);
@@ -229,13 +265,13 @@ static void sbi_tlb_sync(struct sbi_scratch *scratch)
* While we are waiting for remote hart to set the sync,
* consume fifo requests to avoid deadlock.
*/
sbi_tlb_process_count(scratch, 1);
tlb_process_count(scratch, 1);
}
return;
}
static inline int __sbi_tlb_range_check(struct sbi_tlb_info *curr,
static inline int tlb_range_check(struct sbi_tlb_info *curr,
struct sbi_tlb_info *next)
{
unsigned long curr_end;
@@ -278,7 +314,7 @@ static inline int __sbi_tlb_range_check(struct sbi_tlb_info *curr,
* before continuing the while loop. This method is preferred over wfi/ipi because
* of MMIO cost involved in later method.
*/
static int sbi_tlb_update_cb(void *in, void *data)
static int tlb_update_cb(void *in, void *data)
{
struct sbi_tlb_info *curr;
struct sbi_tlb_info *next;
@@ -293,16 +329,16 @@ static int sbi_tlb_update_cb(void *in, void *data)
if (next->local_fn == sbi_tlb_local_sfence_vma_asid &&
curr->local_fn == sbi_tlb_local_sfence_vma_asid) {
if (next->asid == curr->asid)
ret = __sbi_tlb_range_check(curr, next);
ret = tlb_range_check(curr, next);
} else if (next->local_fn == sbi_tlb_local_sfence_vma &&
curr->local_fn == sbi_tlb_local_sfence_vma) {
ret = __sbi_tlb_range_check(curr, next);
ret = tlb_range_check(curr, next);
}
return ret;
}
static int sbi_tlb_update(struct sbi_scratch *scratch,
static int tlb_update(struct sbi_scratch *scratch,
struct sbi_scratch *remote_scratch,
u32 remote_hartid, void *data)
{
@@ -332,7 +368,7 @@ static int sbi_tlb_update(struct sbi_scratch *scratch,
tlb_fifo_r = sbi_scratch_offset_ptr(remote_scratch, tlb_fifo_off);
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, sbi_tlb_update_cb);
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, tlb_update_cb);
if (ret != SBI_FIFO_UNCHANGED) {
return 1;
}
@@ -346,7 +382,7 @@ static int sbi_tlb_update(struct sbi_scratch *scratch,
* TODO: Introduce a wait/wakeup event mechanism to handle
* this properly.
*/
sbi_tlb_process_count(scratch, 1);
tlb_process_count(scratch, 1);
sbi_dprintf("hart%d: hart%d tlb fifo full\n",
curr_hartid, remote_hartid);
}
@@ -356,9 +392,9 @@ static int sbi_tlb_update(struct sbi_scratch *scratch,
static struct sbi_ipi_event_ops tlb_ops = {
.name = "IPI_TLB",
.update = sbi_tlb_update,
.sync = sbi_tlb_sync,
.process = sbi_tlb_process,
.update = tlb_update,
.sync = tlb_sync,
.process = tlb_process,
};
static u32 tlb_event = SBI_IPI_EVENT_MAX;
@@ -368,6 +404,8 @@ int sbi_tlb_request(ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo)
if (!tinfo->local_fn)
return SBI_EINVAL;
tlb_pmu_incr_fw_ctr(tinfo);
return sbi_ipi_send_many(hmask, hbase, tlb_event, tinfo);
}
@@ -380,19 +418,16 @@ int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot)
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
if (cold_boot) {
tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync),
"IPI_TLB_SYNC");
tlb_sync_off = sbi_scratch_alloc_offset(sizeof(*tlb_sync));
if (!tlb_sync_off)
return SBI_ENOMEM;
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q),
"IPI_TLB_FIFO");
tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*tlb_q));
if (!tlb_fifo_off) {
sbi_scratch_free_offset(tlb_sync_off);
return SBI_ENOMEM;
}
tlb_fifo_mem_off = sbi_scratch_alloc_offset(
SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE,
"IPI_TLB_FIFO_MEM");
SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE);
if (!tlb_fifo_mem_off) {
sbi_scratch_free_offset(tlb_fifo_off);
sbi_scratch_free_offset(tlb_sync_off);

View File

@@ -16,6 +16,7 @@
#include <sbi/sbi_illegal_insn.h>
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_misaligned_ldst.h>
#include <sbi/sbi_pmu.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_timer.h>
#include <sbi/sbi_trap.h>
@@ -257,6 +258,11 @@ struct sbi_trap_regs *sbi_trap_handler(struct sbi_trap_regs *regs)
rc = sbi_ecall_handler(regs);
msg = "ecall handler failed";
break;
case CAUSE_LOAD_ACCESS:
case CAUSE_STORE_ACCESS:
sbi_pmu_ctr_incr_fw(mcause == CAUSE_LOAD_ACCESS ?
SBI_PMU_FW_ACCESS_LOAD : SBI_PMU_FW_ACCESS_STORE);
/* fallthrough */
default:
/* If the trap came from S or U mode, redirect it there */
trap.epc = regs->mepc;

View File

@@ -9,6 +9,7 @@
*/
#include <libfdt.h>
#include <libfdt_env.h>
#include <sbi/sbi_domain.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hartmask.h>
@@ -95,7 +96,7 @@ static int __fixup_find_domain_offset(void *fdt, int doff, void *p)
{
struct __fixup_find_domain_offset_info *fdo = p;
if (!sbi_strcmp(fdo->name, fdt_get_name(fdt, doff, NULL)))
if (!strncmp(fdo->name, fdt_get_name(fdt, doff, NULL), strlen(fdo->name)))
*fdo->doffset = doff;
return 0;
@@ -287,7 +288,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
regions = &fdt_regions[fdt_domains_count][0];
/* Read DT node name */
sbi_strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
strncpy(dom->name, fdt_get_name(fdt, domain_offset, NULL),
sizeof(dom->name));
dom->name[sizeof(dom->name) - 1] = '\0';
@@ -313,7 +314,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
/* Setup memregions from DT */
val32 = 0;
sbi_memset(regions, 0,
memset(regions, 0,
sizeof(*regions) * (FDT_DOMAIN_REGION_MAX_COUNT + 1));
dom->regions = regions;
err = fdt_iterate_each_memregion(fdt, domain_offset, &val32,
@@ -337,7 +338,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
continue;
if (FDT_DOMAIN_REGION_MAX_COUNT <= val32)
return SBI_EINVAL;
sbi_memcpy(&regions[val32++], reg, sizeof(*reg));
memcpy(&regions[val32++], reg, sizeof(*reg));
}
/* Read "boot-hart" DT property */

View File

@@ -15,6 +15,7 @@
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_string.h>
#include <sbi_utils/fdt/fdt_fixup.h>
#include <sbi_utils/fdt/fdt_pmu.h>
#include <sbi_utils/fdt/fdt_helper.h>
void fdt_cpu_fixup(void *fdt)
@@ -263,6 +264,7 @@ void fdt_fixups(void *fdt)
fdt_plic_fixup(fdt);
fdt_reserved_memory_fixup(fdt);
fdt_pmu_fixup(fdt);
}

View File

@@ -14,7 +14,6 @@
#include <sbi/sbi_scratch.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/irqchip/plic.h>
#include <sbi_utils/sys/clint.h>
#define DEFAULT_UART_FREQ 0
#define DEFAULT_UART_BAUD 115200
@@ -26,6 +25,8 @@
#define DEFAULT_SIFIVE_UART_REG_SHIFT 0
#define DEFAULT_SIFIVE_UART_REG_IO_WIDTH 4
#define DEFAULT_GAISLER_UART_REG_IO_WIDTH 4
#define DEFAULT_SHAKTI_UART_FREQ 50000000
#define DEFAULT_SHAKTI_UART_BAUD 115200
@@ -71,8 +72,55 @@ int fdt_find_match(void *fdt, int startoff,
return SBI_ENODEV;
}
int fdt_parse_phandle_with_args(void *fdt, int nodeoff,
const char *prop, const char *cells_prop,
int index, struct fdt_phandle_args *out_args)
{
u32 i, pcells;
int len, pnodeoff;
const fdt32_t *list, *list_end, *val;
if (!fdt || (nodeoff < 0) || !prop || !cells_prop || !out_args)
return SBI_EINVAL;
list = fdt_getprop(fdt, nodeoff, prop, &len);
if (!list)
return SBI_ENOENT;
list_end = list + (len / sizeof(*list));
while (list < list_end) {
pnodeoff = fdt_node_offset_by_phandle(fdt,
fdt32_to_cpu(*list));
if (pnodeoff < 0)
return pnodeoff;
list++;
val = fdt_getprop(fdt, pnodeoff, cells_prop, &len);
if (!val)
return SBI_ENOENT;
pcells = fdt32_to_cpu(*val);
if (FDT_MAX_PHANDLE_ARGS < pcells)
return SBI_EINVAL;
if (list + pcells > list_end)
return SBI_ENOENT;
if (index > 0) {
list += pcells;
index--;
} else {
out_args->node_offset = pnodeoff;
out_args->args_count = pcells;
for (i = 0; i < pcells; i++)
out_args->args[i] = fdt32_to_cpu(list[i]);
return 0;
}
}
return SBI_ENOENT;
}
static int fdt_translate_address(void *fdt, uint64_t reg, int parent,
unsigned long *addr)
uint64_t *addr)
{
int i, rlen;
int cell_addr, cell_size;
@@ -109,14 +157,17 @@ static int fdt_translate_address(void *fdt, uint64_t reg, int parent,
return 0;
}
int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
unsigned long *size)
int fdt_get_node_addr_size(void *fdt, int node, int index,
uint64_t *addr, uint64_t *size)
{
int parent, len, i, rc;
int cell_addr, cell_size;
const fdt32_t *prop_addr, *prop_size;
uint64_t temp = 0;
if (!fdt || node < 0 || index < 0)
return SBI_EINVAL;
parent = fdt_parent_offset(fdt, node);
if (parent < 0)
return parent;
@@ -131,6 +182,11 @@ int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr,
prop_addr = fdt_getprop(fdt, node, "reg", &len);
if (!prop_addr)
return SBI_ENODEV;
if ((len / sizeof(u32)) <= (index * (cell_addr + cell_size)))
return SBI_EINVAL;
prop_addr = prop_addr + (index * (cell_addr + cell_size));
prop_size = prop_addr + cell_addr;
if (addr) {
@@ -213,17 +269,78 @@ int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid)
return 0;
}
int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
{
const fdt32_t *val;
int len, cpus_offset;
if (!fdt || !freq)
return SBI_EINVAL;
cpus_offset = fdt_path_offset(fdt, "/cpus");
if (cpus_offset < 0)
return cpus_offset;
val = fdt_getprop(fdt, cpus_offset, "timebase-frequency", &len);
if (len > 0 && val)
*freq = fdt32_to_cpu(*val);
else
return SBI_ENOENT;
return 0;
}
int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
struct platform_uart_data *uart)
{
int len, rc;
const fdt32_t *val;
uint64_t reg_addr, reg_size;
if (nodeoffset < 0 || !uart || !fdt)
return SBI_ENODEV;
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
&reg_addr, &reg_size);
if (rc < 0 || !reg_addr || !reg_size)
return SBI_ENODEV;
uart->addr = reg_addr;
/**
* UART address is mandatory. clock-frequency and current-speed
* may not be present. Don't return error.
*/
val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
if (len > 0 && val)
uart->freq = fdt32_to_cpu(*val);
else
uart->freq = DEFAULT_UART_FREQ;
val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
if (len > 0 && val)
uart->baud = fdt32_to_cpu(*val);
else
uart->baud = DEFAULT_UART_BAUD;
/* For Gaisler APBUART, the reg-shift and reg-io-width are fixed .*/
uart->reg_shift = DEFAULT_UART_REG_SHIFT;
uart->reg_io_width = DEFAULT_GAISLER_UART_REG_IO_WIDTH;
return 0;
}
int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
struct platform_uart_data *uart)
{
int len, rc;
const fdt32_t *val;
unsigned long reg_addr, reg_size;
uint64_t reg_addr, reg_size;
if (nodeoffset < 0 || !uart || !fdt)
return SBI_ENODEV;
rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
&reg_addr, &reg_size);
if (rc < 0 || !reg_addr || !reg_size)
return SBI_ENODEV;
uart->addr = reg_addr;
@@ -252,12 +369,13 @@ int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
{
int len, rc;
const fdt32_t *val;
unsigned long reg_addr, reg_size;
uint64_t reg_addr, reg_size;
if (nodeoffset < 0 || !uart || !fdt)
return SBI_ENODEV;
rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
&reg_addr, &reg_size);
if (rc < 0 || !reg_addr || !reg_size)
return SBI_ENODEV;
uart->addr = reg_addr;
@@ -290,12 +408,13 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
{
int len, rc;
const fdt32_t *val;
unsigned long reg_addr, reg_size;
uint64_t reg_addr, reg_size;
if (nodeoffset < 0 || !uart || !fdt)
return SBI_ENODEV;
rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
&reg_addr, &reg_size);
if (rc < 0 || !reg_addr || !reg_size)
return SBI_ENODEV;
uart->addr = reg_addr;
@@ -350,12 +469,13 @@ int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic)
{
int len, rc;
const fdt32_t *val;
unsigned long reg_addr, reg_size;
uint64_t reg_addr, reg_size;
if (nodeoffset < 0 || !plic || !fdt)
return SBI_ENODEV;
rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
&reg_addr, &reg_size);
if (rc < 0 || !reg_addr || !reg_size)
return SBI_ENODEV;
plic->addr = reg_addr;
@@ -381,34 +501,51 @@ int fdt_parse_plic(void *fdt, struct plic_data *plic, const char *compat)
return fdt_parse_plic_node(fdt, nodeoffset, plic);
}
int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
struct clint_data *clint)
int fdt_parse_aclint_node(void *fdt, int nodeoffset, bool for_timer,
unsigned long *out_addr1, unsigned long *out_size1,
unsigned long *out_addr2, unsigned long *out_size2,
u32 *out_first_hartid, u32 *out_hart_count)
{
const fdt32_t *val;
unsigned long reg_addr, reg_size;
uint64_t reg_addr, reg_size;
int i, rc, count, cpu_offset, cpu_intc_offset;
u32 phandle, hwirq, hartid, first_hartid, last_hartid;
u32 phandle, hwirq, hartid, first_hartid, last_hartid, hart_count;
u32 match_hwirq = (for_timer) ? IRQ_M_TIMER : IRQ_M_SOFT;
if (nodeoffset < 0 || !clint || !fdt)
return SBI_ENODEV;
if (nodeoffset < 0 || !fdt ||
!out_addr1 || !out_size1 ||
!out_first_hartid || !out_hart_count)
return SBI_EINVAL;
rc = fdt_get_node_addr_size(fdt, nodeoffset, &reg_addr, &reg_size);
if (rc < 0 || !reg_addr || !reg_size)
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
&reg_addr, &reg_size);
if (rc < 0 || !reg_size)
return SBI_ENODEV;
clint->addr = reg_addr;
*out_addr1 = reg_addr;
*out_size1 = reg_size;
rc = fdt_get_node_addr_size(fdt, nodeoffset, 1,
&reg_addr, &reg_size);
if (rc < 0 || !reg_size)
reg_addr = reg_size = 0;
if (out_addr2)
*out_addr2 = reg_addr;
if (out_size2)
*out_size2 = reg_size;
*out_first_hartid = 0;
*out_hart_count = 0;
val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count);
if (!val || count < sizeof(fdt32_t))
return SBI_EINVAL;
return 0;
count = count / sizeof(fdt32_t);
first_hartid = -1U;
last_hartid = 0;
clint->hart_count = 0;
for (i = 0; i < count; i += 2) {
phandle = fdt32_to_cpu(val[i]);
hwirq = fdt32_to_cpu(val[i + 1]);
hart_count = last_hartid = 0;
for (i = 0; i < (count / 2); i++) {
phandle = fdt32_to_cpu(val[2 * i]);
hwirq = fdt32_to_cpu(val[(2 * i) + 1]);
cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
if (cpu_intc_offset < 0)
@@ -430,26 +567,20 @@ int fdt_parse_clint_node(void *fdt, int nodeoffset, bool for_timer,
first_hartid = hartid;
if (hartid > last_hartid)
last_hartid = hartid;
clint->hart_count++;
hart_count++;
}
}
if ((last_hartid < first_hartid) || first_hartid == -1U)
return SBI_ENODEV;
clint->first_hartid = first_hartid;
count = last_hartid - first_hartid + 1;
if (clint->hart_count < count)
clint->hart_count = count;
clint->has_64bit_mmio = TRUE;
if (fdt_getprop(fdt, nodeoffset, "clint,has-no-64bit-mmio", &count))
clint->has_64bit_mmio = FALSE;
if ((last_hartid >= first_hartid) && first_hartid != -1U) {
*out_first_hartid = first_hartid;
count = last_hartid - first_hartid + 1;
*out_hart_count = (hart_count < count) ? hart_count : count;
}
return 0;
}
int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
const char *compatible)
{
int nodeoffset, rc;
@@ -458,7 +589,7 @@ int fdt_parse_compat_addr(void *fdt, unsigned long *addr,
if (nodeoffset < 0)
return nodeoffset;
rc = fdt_get_node_addr_size(fdt, nodeoffset, addr, NULL);
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0, addr, NULL);
if (rc < 0 || !addr)
return SBI_ENODEV;

117
lib/utils/fdt/fdt_pmu.c Normal file
View File

@@ -0,0 +1,117 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* fdt_pmu.c - Flat Device Tree PMU helper routines
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <libfdt.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_pmu.h>
#include <sbi_utils/fdt/fdt_helper.h>
#define FDT_PMU_HW_EVENT_MAX (SBI_PMU_HW_EVENT_MAX * 2)
struct fdt_pmu_hw_event_select {
uint32_t eidx;
uint64_t select;
};
static struct fdt_pmu_hw_event_select fdt_pmu_evt_select[FDT_PMU_HW_EVENT_MAX] = {0};
static uint32_t hw_event_count;
uint64_t fdt_pmu_get_select_value(uint32_t event_idx)
{
int i;
struct fdt_pmu_hw_event_select *event;
for (i = 0; i < SBI_PMU_HW_EVENT_MAX; i++) {
event = &fdt_pmu_evt_select[i];
if (event->eidx == event_idx)
return event->select;
}
return 0;
}
int fdt_pmu_fixup(void *fdt)
{
int pmu_offset;
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
if (!fdt)
return SBI_EINVAL;
pmu_offset = fdt_node_offset_by_compatible(fdt, -1, "riscv,pmu");
if (pmu_offset < 0)
return SBI_EFAIL;
fdt_delprop(fdt, pmu_offset, "riscv,event-to-mhpmcounters");
fdt_delprop(fdt, pmu_offset, "riscv,event-to-mhpmevent");
fdt_delprop(fdt, pmu_offset, "riscv,raw-event-to-mhpmcounters");
if (!sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
fdt_delprop(fdt, pmu_offset, "interrupts-extended");
return 0;
}
int fdt_pmu_setup(void *fdt)
{
int i, pmu_offset, len, result;
const u32 *event_val;
const u32 *event_ctr_map;
struct fdt_pmu_hw_event_select *event;
uint64_t raw_selector, select_mask;
u32 event_idx_start, event_idx_end, ctr_map;
if (!fdt)
return SBI_EINVAL;
pmu_offset = fdt_node_offset_by_compatible(fdt, -1, "riscv,pmu");
if (pmu_offset < 0)
return SBI_EFAIL;
event_ctr_map = fdt_getprop(fdt, pmu_offset, "riscv,event-to-mhpmcounters", &len);
if (!event_ctr_map || len < 8)
return SBI_EFAIL;
len = len / (sizeof(u32) * 3);
for (i = 0; i < len; i++) {
event_idx_start = fdt32_to_cpu(event_ctr_map[3 * i]);
event_idx_end = fdt32_to_cpu(event_ctr_map[3 * i + 1]);
ctr_map = fdt32_to_cpu(event_ctr_map[3 * i + 2]);
sbi_pmu_add_hw_event_counter_map(event_idx_start, event_idx_end, ctr_map);
}
event_val = fdt_getprop(fdt, pmu_offset, "riscv,event-to-mhpmevent", &len);
if (!event_val || len < 8)
return SBI_EFAIL;
len = len / (sizeof(u32) * 3);
for (i = 0; i < len; i++) {
event = &fdt_pmu_evt_select[hw_event_count];
event->eidx = fdt32_to_cpu(event_val[3 * i]);
event->select = fdt32_to_cpu(event_val[3 * i + 1]);
event->select = (event->select << 32) | fdt32_to_cpu(event_val[3 * i + 2]);
hw_event_count++;
}
event_val = fdt_getprop(fdt, pmu_offset, "riscv,raw-event-to-mhpmcounters", &len);
if (!event_val || len < 20)
return SBI_EFAIL;
len = len / (sizeof(u32) * 5);
for (i = 0; i < len; i++) {
raw_selector = fdt32_to_cpu(event_val[5 * i]);
raw_selector = (raw_selector << 32) | fdt32_to_cpu(event_val[5 * i + 1]);
select_mask = fdt32_to_cpu(event_val[5 * i + 2]);
select_mask = (select_mask << 32) | fdt32_to_cpu(event_val[5 * i + 3]);
ctr_map = fdt32_to_cpu(event_val[5 * i + 4]);
result = sbi_pmu_add_raw_event_counter_map(raw_selector, select_mask, ctr_map);
if (!result)
hw_event_count++;
}
return 0;
}

View File

@@ -5,5 +5,6 @@
#
libsbiutils-objs-y += fdt/fdt_domain.o
libsbiutils-objs-y += fdt/fdt_pmu.o
libsbiutils-objs-y += fdt/fdt_helper.o
libsbiutils-objs-y += fdt/fdt_fixup.o

135
lib/utils/gpio/fdt_gpio.c Normal file
View File

@@ -0,0 +1,135 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/gpio/fdt_gpio.h>
extern struct fdt_gpio fdt_gpio_sifive;
static struct fdt_gpio *gpio_drivers[] = {
&fdt_gpio_sifive
};
static struct fdt_gpio *fdt_gpio_driver(struct gpio_chip *chip)
{
int pos;
if (!chip)
return NULL;
for (pos = 0; pos < array_size(gpio_drivers); pos++) {
if (chip->driver == gpio_drivers[pos])
return gpio_drivers[pos];
}
return NULL;
}
static int fdt_gpio_init(void *fdt, u32 phandle)
{
int pos, nodeoff, rc;
struct fdt_gpio *drv;
const struct fdt_match *match;
/* Find node offset */
nodeoff = fdt_node_offset_by_phandle(fdt, phandle);
if (nodeoff < 0)
return nodeoff;
/* Check "gpio-controller" property */
if (!fdt_getprop(fdt, nodeoff, "gpio-controller", &rc))
return SBI_EINVAL;
/* Try all GPIO drivers one-by-one */
for (pos = 0; pos < array_size(gpio_drivers); pos++) {
drv = gpio_drivers[pos];
match = fdt_match_node(fdt, nodeoff, drv->match_table);
if (match && drv->init) {
rc = drv->init(fdt, nodeoff, phandle, match);
if (rc == SBI_ENODEV)
continue;
if (rc)
return rc;
return 0;
}
}
return SBI_ENOSYS;
}
static int fdt_gpio_chip_find(void *fdt, u32 phandle,
struct gpio_chip **out_chip)
{
int rc;
struct gpio_chip *chip = gpio_chip_find(phandle);
if (!chip) {
/* GPIO chip not found so initialize matching driver */
rc = fdt_gpio_init(fdt, phandle);
if (rc)
return rc;
/* Try to find GPIO chip again */
chip = gpio_chip_find(phandle);
if (!chip)
return SBI_ENOSYS;
}
if (out_chip)
*out_chip = chip;
return 0;
}
int fdt_gpio_pin_get(void *fdt, int nodeoff, int index,
struct gpio_pin *out_pin)
{
int rc;
u32 phandle;
struct fdt_gpio *drv;
struct gpio_chip *chip = NULL;
struct fdt_phandle_args pargs;
if (!fdt || (nodeoff < 0) || (index < 0) || !out_pin)
return SBI_EINVAL;
pargs.node_offset = pargs.args_count = 0;
rc = fdt_parse_phandle_with_args(fdt, nodeoff,
"gpios", "#gpio-cells",
index, &pargs);
if (rc)
return rc;
phandle = fdt_get_phandle(fdt, pargs.node_offset);
rc = fdt_gpio_chip_find(fdt, phandle, &chip);
if (rc)
return rc;
drv = fdt_gpio_driver(chip);
if (!drv || !drv->xlate)
return SBI_ENOSYS;
return drv->xlate(chip, &pargs, out_pin);
}
int fdt_gpio_simple_xlate(struct gpio_chip *chip,
const struct fdt_phandle_args *pargs,
struct gpio_pin *out_pin)
{
if ((pargs->args_count < 2) || (chip->ngpio <= pargs->args[0]))
return SBI_EINVAL;
out_pin->chip = chip;
out_pin->offset = pargs->args[0];
out_pin->flags = pargs->args[1];
return 0;
}

View File

@@ -0,0 +1,107 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 SiFive
*
* Authors:
* Green Wan <green.wan@sifive.com>
*/
#include <sbi/riscv_io.h>
#include <sbi/sbi_error.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/gpio/fdt_gpio.h>
#define SIFIVE_GPIO_CHIP_MAX 2
#define SIFIVE_GPIO_PINS_MIN 1
#define SIFIVE_GPIO_PINS_MAX 32
#define SIFIVE_GPIO_PINS_DEF 16
#define SIFIVE_GPIO_OUTEN 0x8
#define SIFIVE_GPIO_OUTVAL 0xc
#define SIFIVE_GPIO_BIT(b) (1U << (b))
struct sifive_gpio_chip {
unsigned long addr;
struct gpio_chip chip;
};
static unsigned int sifive_gpio_chip_count;
static struct sifive_gpio_chip sifive_gpio_chip_array[SIFIVE_GPIO_CHIP_MAX];
static int sifive_gpio_direction_output(struct gpio_pin *gp, int value)
{
unsigned int v;
struct sifive_gpio_chip *chip =
container_of(gp->chip, struct sifive_gpio_chip, chip);
v = readl((volatile void *)(chip->addr + SIFIVE_GPIO_OUTEN));
v |= SIFIVE_GPIO_BIT(gp->offset);
writel(v, (volatile void *)(chip->addr + SIFIVE_GPIO_OUTEN));
v = readl((volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
if (!value)
v &= ~SIFIVE_GPIO_BIT(gp->offset);
else
v |= SIFIVE_GPIO_BIT(gp->offset);
writel(v, (volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
return 0;
}
static void sifive_gpio_set(struct gpio_pin *gp, int value)
{
unsigned int v;
struct sifive_gpio_chip *chip =
container_of(gp->chip, struct sifive_gpio_chip, chip);
v = readl((volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
if (!value)
v &= ~SIFIVE_GPIO_BIT(gp->offset);
else
v |= SIFIVE_GPIO_BIT(gp->offset);
writel(v, (volatile void *)(chip->addr + SIFIVE_GPIO_OUTVAL));
}
extern struct fdt_gpio fdt_gpio_sifive;
static int sifive_gpio_init(void *fdt, int nodeoff, u32 phandle,
const struct fdt_match *match)
{
int rc;
struct sifive_gpio_chip *chip;
uint64_t addr;
if (SIFIVE_GPIO_CHIP_MAX <= sifive_gpio_chip_count)
return SBI_ENOSPC;
chip = &sifive_gpio_chip_array[sifive_gpio_chip_count];
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
if (rc)
return rc;
chip->addr = addr;
chip->chip.driver = &fdt_gpio_sifive;
chip->chip.id = phandle;
chip->chip.ngpio = SIFIVE_GPIO_PINS_DEF;
chip->chip.direction_output = sifive_gpio_direction_output;
chip->chip.set = sifive_gpio_set;
rc = gpio_chip_add(&chip->chip);
if (rc)
return rc;
sifive_gpio_chip_count++;
return 0;
}
static const struct fdt_match sifive_gpio_match[] = {
{ .compatible = "sifive,gpio0" },
{ },
};
struct fdt_gpio fdt_gpio_sifive = {
.match_table = sifive_gpio_match,
.xlate = fdt_gpio_simple_xlate,
.init = sifive_gpio_init,
};

98
lib/utils/gpio/gpio.c Normal file
View File

@@ -0,0 +1,98 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/sbi_error.h>
#include <sbi_utils/gpio/gpio.h>
static SBI_LIST_HEAD(gpio_chip_list);
struct gpio_chip *gpio_chip_find(unsigned int id)
{
struct sbi_dlist *pos;
sbi_list_for_each(pos, &(gpio_chip_list)) {
struct gpio_chip *chip = to_gpio_chip(pos);
if (chip->id == id)
return chip;
}
return NULL;
}
int gpio_chip_add(struct gpio_chip *gc)
{
if (!gc)
return SBI_EINVAL;
if (gpio_chip_find(gc->id))
return SBI_EALREADY;
sbi_list_add(&(gc->node), &(gpio_chip_list));
return 0;
}
void gpio_chip_remove(struct gpio_chip *gc)
{
if (!gc)
return;
sbi_list_del(&(gc->node));
}
int gpio_get_direction(struct gpio_pin *gp)
{
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
return SBI_EINVAL;
if (!gp->chip->get_direction)
return SBI_ENOSYS;
return gp->chip->get_direction(gp);
}
int gpio_direction_input(struct gpio_pin *gp)
{
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
return SBI_EINVAL;
if (!gp->chip->direction_input)
return SBI_ENOSYS;
return gp->chip->direction_input(gp);
}
int gpio_direction_output(struct gpio_pin *gp, int value)
{
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
return SBI_EINVAL;
if (!gp->chip->direction_output)
return SBI_ENOSYS;
return gp->chip->direction_output(gp, value);
}
int gpio_get(struct gpio_pin *gp)
{
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
return SBI_EINVAL;
if (!gp->chip->get)
return SBI_ENOSYS;
return gp->chip->get(gp);
}
int gpio_set(struct gpio_pin *gp, int value)
{
if (!gp || !gp->chip || (gp->chip->ngpio <= gp->offset))
return SBI_EINVAL;
if (!gp->chip->set)
return SBI_ENOSYS;
gp->chip->set(gp, value);
return 0;
}

12
lib/utils/gpio/objects.mk Normal file
View File

@@ -0,0 +1,12 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2021 Western Digital Corporation or its affiliates.
#
# Authors:
# Anup Patel <anup.patel@wdc.com>
#
libsbiutils-objs-y += gpio/fdt_gpio.o
libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
libsbiutils-objs-y += gpio/gpio.o

90
lib/utils/i2c/fdt_i2c.c Normal file
View File

@@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 YADRO
*
* Authors:
* Nikita Shubin <n.shubin@yadro.com>
*
* derivate: lib/utils/gpio/fdt_gpio.c
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/i2c/fdt_i2c.h>
#include <sbi/sbi_console.h>
extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
static struct fdt_i2c_adapter *i2c_adapter_drivers[] = {
&fdt_i2c_adapter_sifive
};
static int fdt_i2c_adapter_init(void *fdt, int nodeoff)
{
int pos, rc;
struct fdt_i2c_adapter *drv;
const struct fdt_match *match;
/* Try all I2C drivers one-by-one */
for (pos = 0; pos < array_size(i2c_adapter_drivers); pos++) {
drv = i2c_adapter_drivers[pos];
match = fdt_match_node(fdt, nodeoff, drv->match_table);
if (match && drv->init) {
rc = drv->init(fdt, nodeoff, match);
if (rc == SBI_ENODEV)
continue;
if (rc)
return rc;
return 0;
}
}
return SBI_ENOSYS;
}
static int fdt_i2c_adapter_find(void *fdt, int nodeoff,
struct i2c_adapter **out_adapter)
{
int rc;
struct i2c_adapter *adapter = i2c_adapter_find(nodeoff);
if (!adapter) {
/* I2C adapter not found so initialize matching driver */
rc = fdt_i2c_adapter_init(fdt, nodeoff);
if (rc)
return rc;
/* Try to find I2C adapter again */
adapter = i2c_adapter_find(nodeoff);
if (!adapter)
return SBI_ENOSYS;
}
if (out_adapter)
*out_adapter = adapter;
return 0;
}
int fdt_i2c_adapter_get(void *fdt, int nodeoff,
struct i2c_adapter **out_adapter)
{
int rc;
struct i2c_adapter *adapter;
if (!fdt || (nodeoff < 0) || !out_adapter)
return SBI_EINVAL;
rc = fdt_i2c_adapter_find(fdt, nodeoff, &adapter);
if (rc)
return rc;
*out_adapter = adapter;
return 0;
}

View File

@@ -0,0 +1,277 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 YADRO
*
* Authors:
* Nikita Shubin <n.shubin@yadro.com>
*/
#include <sbi/riscv_io.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_timer.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/i2c/fdt_i2c.h>
#define SIFIVE_I2C_ADAPTER_MAX 2
#define SIFIVE_I2C_PRELO 0x00
#define SIFIVE_I2C_PREHI 0x04
#define SIFIVE_I2C_CTR 0x08
#define SIFIVE_I2C_TXR 0x00c
#define SIFIVE_I2C_RXR SIFIVE_I2C_TXR
#define SIFIVE_I2C_CR 0x010
#define SIFIVE_I2C_SR SIFIVE_I2C_CR
#define SIFIVE_I2C_CTR_IEN (1 << 6)
#define SIFIVE_I2C_CTR_EN (1 << 7)
#define SIFIVE_I2C_CMD_IACK (1 << 0)
#define SIFIVE_I2C_CMD_ACK (1 << 3)
#define SIFIVE_I2C_CMD_WR (1 << 4)
#define SIFIVE_I2C_CMD_RD (1 << 5)
#define SIFIVE_I2C_CMD_STO (1 << 6)
#define SIFIVE_I2C_CMD_STA (1 << 7)
#define SIFIVE_I2C_STATUS_IF (1 << 0)
#define SIFIVE_I2C_STATUS_TIP (1 << 1)
#define SIFIVE_I2C_STATUS_AL (1 << 5)
#define SIFIVE_I2C_STATUS_BUSY (1 << 6)
#define SIFIVE_I2C_STATUS_RXACK (1 << 7)
#define SIFIVE_I2C_WRITE_BIT (0 << 0)
#define SIFIVE_I2C_READ_BIT (1 << 0)
struct sifive_i2c_adapter {
unsigned long addr;
struct i2c_adapter adapter;
};
static unsigned int sifive_i2c_adapter_count;
static struct sifive_i2c_adapter
sifive_i2c_adapter_array[SIFIVE_I2C_ADAPTER_MAX];
extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
static inline void sifive_i2c_setreg(struct sifive_i2c_adapter *adap,
uint8_t reg, uint8_t value)
{
writel(value, (volatile void *)adap->addr + reg);
}
static inline uint8_t sifive_i2c_getreg(struct sifive_i2c_adapter *adap,
uint8_t reg)
{
return readl((volatile void *)adap->addr + reg);
}
static int sifive_i2c_adapter_rxack(struct sifive_i2c_adapter *adap)
{
uint8_t val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
if (val & SIFIVE_I2C_STATUS_RXACK)
return SBI_EIO;
return 0;
}
static int sifive_i2c_adapter_poll(struct sifive_i2c_adapter *adap,
uint32_t mask)
{
unsigned int timeout = 1; // [msec]
int count = 0;
uint8_t val;
sbi_timer_udelay(80); // worst case if bus speed is 100 kHz
do {
val = sifive_i2c_getreg(adap, SIFIVE_I2C_SR);
if (!(val & mask))
return 0;
sbi_timer_udelay(1);
count += 1;
if (count == (timeout * 1000))
return SBI_ETIMEDOUT;
} while (1);
}
#define sifive_i2c_adapter_poll_tip(adap) \
sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_TIP)
#define sifive_i2c_adapter_poll_busy(adap) \
sifive_i2c_adapter_poll(adap, SIFIVE_I2C_STATUS_BUSY)
static int sifive_i2c_adapter_start(struct sifive_i2c_adapter *adap,
uint8_t addr, uint8_t bit)
{
uint8_t val = (addr << 1) | bit;
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, val);
val = SIFIVE_I2C_CMD_STA | SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK;
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, val);
return sifive_i2c_adapter_poll_tip(adap);
}
static int sifive_i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr,
uint8_t reg, uint8_t *buffer, int len)
{
struct sifive_i2c_adapter *adap =
container_of(ia, struct sifive_i2c_adapter, adapter);
int rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
if (rc)
return rc;
rc = sifive_i2c_adapter_rxack(adap);
if (rc)
return rc;
/* set register address */
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
SIFIVE_I2C_CMD_IACK);
rc = sifive_i2c_adapter_poll_tip(adap);
if (rc)
return rc;
rc = sifive_i2c_adapter_rxack(adap);
if (rc)
return rc;
/* set value */
while (len) {
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
SIFIVE_I2C_CMD_IACK);
rc = sifive_i2c_adapter_poll_tip(adap);
if (rc)
return rc;
rc = sifive_i2c_adapter_rxack(adap);
if (rc)
return rc;
buffer++;
len--;
}
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
SIFIVE_I2C_CMD_IACK);
/* poll BUSY instead of ACK*/
rc = sifive_i2c_adapter_poll_busy(adap);
if (rc)
return rc;
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
return 0;
}
static int sifive_i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr,
uint8_t reg, uint8_t *buffer, int len)
{
struct sifive_i2c_adapter *adap =
container_of(ia, struct sifive_i2c_adapter, adapter);
int rc;
rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
if (rc)
return rc;
rc = sifive_i2c_adapter_rxack(adap);
if (rc)
return rc;
sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
SIFIVE_I2C_CMD_IACK);
rc = sifive_i2c_adapter_poll_tip(adap);
if (rc)
return rc;
rc = sifive_i2c_adapter_rxack(adap);
if (rc)
return rc;
/* setting addr with high 0 bit */
rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_READ_BIT);
if (rc)
return rc;
rc = sifive_i2c_adapter_rxack(adap);
if (rc)
return rc;
while (len) {
if (len == 1)
sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
SIFIVE_I2C_CMD_ACK |
SIFIVE_I2C_CMD_RD |
SIFIVE_I2C_CMD_IACK);
else
sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
SIFIVE_I2C_CMD_RD |
SIFIVE_I2C_CMD_IACK);
rc = sifive_i2c_adapter_poll_tip(adap);
if (rc)
return rc;
*buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
buffer++;
len--;
}
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
SIFIVE_I2C_CMD_IACK);
rc = sifive_i2c_adapter_poll_busy(adap);
if (rc)
return rc;
sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
return 0;
}
static int sifive_i2c_init(void *fdt, int nodeoff,
const struct fdt_match *match)
{
int rc;
struct sifive_i2c_adapter *adapter;
uint64_t addr;
if (sifive_i2c_adapter_count >= SIFIVE_I2C_ADAPTER_MAX)
return SBI_ENOSPC;
adapter = &sifive_i2c_adapter_array[sifive_i2c_adapter_count];
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
if (rc)
return rc;
adapter->addr = addr;
adapter->adapter.driver = &fdt_i2c_adapter_sifive;
adapter->adapter.id = nodeoff;
adapter->adapter.write = sifive_i2c_adapter_write;
adapter->adapter.read = sifive_i2c_adapter_read;
rc = i2c_adapter_add(&adapter->adapter);
if (rc)
return rc;
sifive_i2c_adapter_count++;
return 0;
}
static const struct fdt_match sifive_i2c_match[] = {
{ .compatible = "sifive,i2c0" },
{ },
};
struct fdt_i2c_adapter fdt_i2c_adapter_sifive = {
.match_table = sifive_i2c_match,
.init = sifive_i2c_init,
};

75
lib/utils/i2c/i2c.c Normal file
View File

@@ -0,0 +1,75 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 YADRO
*
* Authors:
* Nikita Shubin <n.shubin@yadro.com>
*
* derivate: lib/utils/gpio/gpio.c
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/sbi_error.h>
#include <sbi_utils/i2c/i2c.h>
static SBI_LIST_HEAD(i2c_adapters_list);
struct i2c_adapter *i2c_adapter_find(int id)
{
struct sbi_dlist *pos;
sbi_list_for_each(pos, &(i2c_adapters_list)) {
struct i2c_adapter *adap = to_i2c_adapter(pos);
if (adap->id == id)
return adap;
}
return NULL;
}
int i2c_adapter_add(struct i2c_adapter *ia)
{
if (!ia)
return SBI_EINVAL;
if (i2c_adapter_find(ia->id))
return SBI_EALREADY;
sbi_list_add(&(ia->node), &(i2c_adapters_list));
return 0;
}
void i2c_adapter_remove(struct i2c_adapter *ia)
{
if (!ia)
return;
sbi_list_del(&(ia->node));
}
int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
uint8_t *buffer, int len)
{
if (!ia)
return SBI_EINVAL;
if (!ia->write)
return SBI_ENOSYS;
return ia->write(ia, addr, reg, buffer, len);
}
int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
uint8_t *buffer, int len)
{
if (!ia)
return SBI_EINVAL;
if (!ia->read)
return SBI_ENOSYS;
return ia->read(ia, addr, reg, buffer, len);
}

12
lib/utils/i2c/objects.mk Normal file
View File

@@ -0,0 +1,12 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2021 YADRO
#
# Authors:
# Nikita Shubin <n.shubin@yadro.com>
#
libsbiutils-objs-y += i2c/i2c.o
libsbiutils-objs-y += i2c/fdt_i2c.o
libsbiutils-objs-y += i2c/fdt_i2c_sifive.o

100
lib/utils/ipi/aclint_mswi.c Normal file
View File

@@ -0,0 +1,100 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/riscv_asm.h>
#include <sbi/riscv_atomic.h>
#include <sbi/riscv_io.h>
#include <sbi/sbi_domain.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hartmask.h>
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_timer.h>
#include <sbi_utils/ipi/aclint_mswi.h>
static struct aclint_mswi_data *mswi_hartid2data[SBI_HARTMASK_MAX_BITS];
static void mswi_ipi_send(u32 target_hart)
{
u32 *msip;
struct aclint_mswi_data *mswi;
if (SBI_HARTMASK_MAX_BITS <= target_hart)
return;
mswi = mswi_hartid2data[target_hart];
if (!mswi)
return;
/* Set ACLINT IPI */
msip = (void *)mswi->addr;
writel(1, &msip[target_hart - mswi->first_hartid]);
}
static void mswi_ipi_clear(u32 target_hart)
{
u32 *msip;
struct aclint_mswi_data *mswi;
if (SBI_HARTMASK_MAX_BITS <= target_hart)
return;
mswi = mswi_hartid2data[target_hart];
if (!mswi)
return;
/* Clear ACLINT IPI */
msip = (void *)mswi->addr;
writel(0, &msip[target_hart - mswi->first_hartid]);
}
static struct sbi_ipi_device aclint_mswi = {
.name = "aclint-mswi",
.ipi_send = mswi_ipi_send,
.ipi_clear = mswi_ipi_clear
};
int aclint_mswi_warm_init(void)
{
/* Clear IPI for current HART */
mswi_ipi_clear(current_hartid());
return 0;
}
int aclint_mswi_cold_init(struct aclint_mswi_data *mswi)
{
u32 i;
int rc;
unsigned long pos, region_size;
struct sbi_domain_memregion reg;
/* Sanity checks */
if (!mswi || (mswi->addr & (ACLINT_MSWI_ALIGN - 1)) ||
(mswi->size < ACLINT_MSWI_SIZE) ||
(mswi->first_hartid >= SBI_HARTMASK_MAX_BITS) ||
(mswi->hart_count > ACLINT_MSWI_MAX_HARTS))
return SBI_EINVAL;
/* Update MSWI hartid table */
for (i = 0; i < mswi->hart_count; i++)
mswi_hartid2data[mswi->first_hartid + i] = mswi;
/* Add MSWI regions to the root domain */
for (pos = 0; pos < mswi->size; pos += ACLINT_MSWI_ALIGN) {
region_size = ((mswi->size - pos) < ACLINT_MSWI_ALIGN) ?
(mswi->size - pos) : ACLINT_MSWI_ALIGN;
sbi_domain_memregion_init(mswi->addr + pos, region_size,
SBI_DOMAIN_MEMREGION_MMIO, &reg);
rc = sbi_domain_root_add_memregion(&reg);
if (rc)
return rc;
}
sbi_ipi_set_device(&aclint_mswi);
return 0;
}

View File

@@ -7,14 +7,15 @@
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/sbi_error.h>
#include <sbi/sbi_scratch.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/ipi/fdt_ipi.h>
extern struct fdt_ipi fdt_ipi_clint;
extern struct fdt_ipi fdt_ipi_mswi;
static struct fdt_ipi *ipi_drivers[] = {
&fdt_ipi_clint
&fdt_ipi_mswi
};
static struct fdt_ipi dummy = {
@@ -44,7 +45,7 @@ static int fdt_ipi_cold_init(void)
int pos, noff, rc;
struct fdt_ipi *drv;
const struct fdt_match *match;
void *fdt = sbi_scratch_thishart_arg1_ptr();
void *fdt = fdt_get_address();
for (pos = 0; pos < array_size(ipi_drivers); pos++) {
drv = ipi_drivers[pos];
@@ -54,6 +55,8 @@ static int fdt_ipi_cold_init(void)
drv->match_table, &match)) >= 0) {
if (drv->cold_init) {
rc = drv->cold_init(fdt, noff, match);
if (rc == SBI_ENODEV)
continue;
if (rc)
return rc;
}

View File

@@ -1,48 +0,0 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/sbi_error.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/ipi/fdt_ipi.h>
#include <sbi_utils/sys/clint.h>
#define CLINT_IPI_MAX_NR 16
static unsigned long clint_ipi_count = 0;
static struct clint_data clint_ipi[CLINT_IPI_MAX_NR];
static int ipi_clint_cold_init(void *fdt, int nodeoff,
const struct fdt_match *match)
{
int rc;
struct clint_data *ci;
if (CLINT_IPI_MAX_NR <= clint_ipi_count)
return SBI_ENOSPC;
ci = &clint_ipi[clint_ipi_count++];
rc = fdt_parse_clint_node(fdt, nodeoff, FALSE, ci);
if (rc)
return rc;
return clint_cold_ipi_init(ci);
}
static const struct fdt_match ipi_clint_match[] = {
{ .compatible = "riscv,clint0" },
{ .compatible = "sifive,clint0" },
{ },
};
struct fdt_ipi fdt_ipi_clint = {
.match_table = ipi_clint_match,
.cold_init = ipi_clint_cold_init,
.warm_init = clint_warm_ipi_init,
.exit = NULL,
};

View File

@@ -0,0 +1,68 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/sbi_error.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/ipi/fdt_ipi.h>
#include <sbi_utils/ipi/aclint_mswi.h>
#define MSWI_MAX_NR 16
static unsigned long mswi_count = 0;
static struct aclint_mswi_data mswi[MSWI_MAX_NR];
static int ipi_mswi_cold_init(void *fdt, int nodeoff,
const struct fdt_match *match)
{
int rc;
unsigned long offset;
struct aclint_mswi_data *ms;
if (MSWI_MAX_NR <= mswi_count)
return SBI_ENOSPC;
ms = &mswi[mswi_count];
rc = fdt_parse_aclint_node(fdt, nodeoff, false,
&ms->addr, &ms->size, NULL, NULL,
&ms->first_hartid, &ms->hart_count);
if (rc)
return rc;
if (match->data) {
/* Adjust MSWI address and size for CLINT device */
offset = *((unsigned long *)match->data);
ms->addr += offset;
if ((ms->size - offset) < ACLINT_MSWI_SIZE)
return SBI_EINVAL;
ms->size = ACLINT_MSWI_SIZE;
}
rc = aclint_mswi_cold_init(ms);
if (rc)
return rc;
mswi_count++;
return 0;
}
static unsigned long clint_offset = CLINT_MSWI_OFFSET;
static const struct fdt_match ipi_mswi_match[] = {
{ .compatible = "riscv,clint0", .data = &clint_offset },
{ .compatible = "sifive,clint0", .data = &clint_offset },
{ .compatible = "riscv,aclint-mswi" },
{ },
};
struct fdt_ipi fdt_ipi_mswi = {
.match_table = ipi_mswi_match,
.cold_init = ipi_mswi_cold_init,
.warm_init = aclint_mswi_warm_init,
.exit = NULL,
};

View File

@@ -7,5 +7,6 @@
# Anup Patel <anup.patel@wdc.com>
#
libsbiutils-objs-y += ipi/aclint_mswi.o
libsbiutils-objs-y += ipi/fdt_ipi.o
libsbiutils-objs-y += ipi/fdt_ipi_clint.o
libsbiutils-objs-y += ipi/fdt_ipi_mswi.o

View File

@@ -7,6 +7,7 @@
* Anup Patel <anup.patel@wdc.com>
*/
#include <sbi/sbi_error.h>
#include <sbi/sbi_scratch.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/irqchip/fdt_irqchip.h>
@@ -37,7 +38,7 @@ static int fdt_irqchip_cold_init(void)
int pos, noff, rc;
struct fdt_irqchip *drv;
const struct fdt_match *match;
void *fdt = sbi_scratch_thishart_arg1_ptr();
void *fdt = fdt_get_address();
for (pos = 0; pos < array_size(irqchip_drivers); pos++) {
drv = irqchip_drivers[pos];
@@ -47,6 +48,8 @@ static int fdt_irqchip_cold_init(void)
drv->match_table, &match)) >= 0) {
if (drv->cold_init) {
rc = drv->cold_init(fdt, noff, match);
if (rc == SBI_ENODEV)
continue;
if (rc)
return rc;
}

View File

@@ -9,6 +9,7 @@
#include <libfdt.h>
#include <sbi/riscv_asm.h>
#include <sbi/riscv_io.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hartmask.h>
#include <sbi_utils/fdt/fdt_helper.h>
@@ -91,6 +92,11 @@ static int irqchip_plic_cold_init(void *fdt, int nodeoff,
if (rc)
return rc;
if (match->data) {
void (*plic_plat_init)(struct plic_data *) = match->data;
plic_plat_init(pd);
}
rc = plic_cold_irqchip_init(pd);
if (rc)
return rc;
@@ -106,9 +112,18 @@ static int irqchip_plic_cold_init(void *fdt, int nodeoff,
return irqchip_plic_update_hartid_table(fdt, nodeoff, pd);
}
#define THEAD_PLIC_CTRL_REG 0x1ffffc
static void thead_plic_plat_init(struct plic_data *pd)
{
writel_relaxed(BIT(0), (void *)pd->addr + THEAD_PLIC_CTRL_REG);
}
static const struct fdt_match irqchip_plic_match[] = {
{ .compatible = "riscv,plic0" },
{ .compatible = "sifive,plic-1.0.0" },
{ .compatible = "thead,c900-plic",
.data = thead_plic_plat_init },
{ },
};

View File

@@ -22,6 +22,10 @@ int32_t fdt_ro_probe_(const void *fdt)
if (can_assume(VALID_DTB))
return totalsize;
/* The device tree must be at an 8-byte aligned address */
if ((uintptr_t)fdt & 7)
return -FDT_ERR_ALIGNMENT;
if (fdt_magic(fdt) == FDT_MAGIC) {
/* Complete tree */
if (!can_assume(LATEST)) {
@@ -86,6 +90,10 @@ int fdt_check_header(const void *fdt)
{
size_t hdrsize;
/* The device tree must be at an 8-byte aligned address */
if ((uintptr_t)fdt & 7)
return -FDT_ERR_ALIGNMENT;
if (fdt_magic(fdt) != FDT_MAGIC)
return -FDT_ERR_BADMAGIC;
if (!can_assume(LATEST)) {
@@ -134,16 +142,20 @@ int fdt_check_header(const void *fdt)
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
{
unsigned absoffset = offset + fdt_off_dt_struct(fdt);
unsigned int uoffset = offset;
unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
if (offset < 0)
return NULL;
if (!can_assume(VALID_INPUT))
if ((absoffset < offset)
if ((absoffset < uoffset)
|| ((absoffset + len) < absoffset)
|| (absoffset + len) > fdt_totalsize(fdt))
return NULL;
if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
if (((offset + len) < offset)
if (((uoffset + len) < uoffset)
|| ((offset + len) > fdt_size_dt_struct(fdt)))
return NULL;
@@ -206,10 +218,11 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
int fdt_check_node_offset_(const void *fdt, int offset)
{
if (can_assume(VALID_INPUT))
return offset;
if ((offset < 0) || (offset % FDT_TAGSIZE)
|| (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
if (!can_assume(VALID_INPUT)
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
return -FDT_ERR_BADOFFSET;
if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
return -FDT_ERR_BADOFFSET;
return offset;
@@ -217,8 +230,11 @@ int fdt_check_node_offset_(const void *fdt, int offset)
int fdt_check_prop_offset_(const void *fdt, int offset)
{
if ((offset < 0) || (offset % FDT_TAGSIZE)
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
if (!can_assume(VALID_INPUT)
&& ((offset < 0) || (offset % FDT_TAGSIZE)))
return -FDT_ERR_BADOFFSET;
if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
return -FDT_ERR_BADOFFSET;
return offset;
@@ -306,9 +322,12 @@ const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
int fdt_move(const void *fdt, void *buf, int bufsize)
{
if (!can_assume(VALID_INPUT) && bufsize < 0)
return -FDT_ERR_NOSPACE;
FDT_RO_PROBE(fdt);
if (fdt_totalsize(fdt) > bufsize)
if (fdt_totalsize(fdt) > (unsigned int)bufsize)
return -FDT_ERR_NOSPACE;
memmove(buf, fdt, fdt_totalsize(fdt));

View File

@@ -7,7 +7,7 @@
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
*/
#ifndef __ASSEMBLER__
#ifndef __ASSEMBLY__
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC */

View File

@@ -19,9 +19,12 @@ int fdt_check_full(const void *fdt, size_t bufsize)
unsigned int depth = 0;
const void *prop;
const char *propname;
bool expect_end = false;
if (bufsize < FDT_V1_SIZE)
return -FDT_ERR_TRUNCATED;
if (bufsize < fdt_header_size(fdt))
return -FDT_ERR_TRUNCATED;
err = fdt_check_header(fdt);
if (err != 0)
return err;
@@ -39,6 +42,10 @@ int fdt_check_full(const void *fdt, size_t bufsize)
if (nextoffset < 0)
return nextoffset;
/* If we see two root nodes, something is wrong */
if (expect_end && tag != FDT_END)
return -FDT_ERR_BADSTRUCTURE;
switch (tag) {
case FDT_NOP:
break;
@@ -52,12 +59,24 @@ int fdt_check_full(const void *fdt, size_t bufsize)
depth++;
if (depth > INT_MAX)
return -FDT_ERR_BADSTRUCTURE;
/* The root node must have an empty name */
if (depth == 1) {
const char *name;
int len;
name = fdt_get_name(fdt, offset, &len);
if (*name || len)
return -FDT_ERR_BADSTRUCTURE;
}
break;
case FDT_END_NODE:
if (depth == 0)
return -FDT_ERR_BADSTRUCTURE;
depth--;
if (depth == 0)
expect_end = true;
break;
case FDT_PROP:

View File

@@ -241,6 +241,7 @@ static int overlay_update_local_node_references(void *fdto,
if (fixup_len % sizeof(uint32_t))
return -FDT_ERR_BADOVERLAY;
fixup_len /= sizeof(uint32_t);
tree_val = fdt_getprop(fdto, tree_node, name, &tree_len);
if (!tree_val) {
@@ -250,7 +251,7 @@ static int overlay_update_local_node_references(void *fdto,
return tree_len;
}
for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) {
for (i = 0; i < fixup_len; i++) {
fdt32_t adj_val;
uint32_t poffset;

View File

@@ -53,7 +53,7 @@ const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
err = -FDT_ERR_BADOFFSET;
absoffset = stroffset + fdt_off_dt_strings(fdt);
if (absoffset >= totalsize)
if (absoffset >= (unsigned)totalsize)
goto fail;
len = totalsize - absoffset;
@@ -61,17 +61,19 @@ const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
if (stroffset < 0)
goto fail;
if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
if (stroffset >= fdt_size_dt_strings(fdt))
if ((unsigned)stroffset >= fdt_size_dt_strings(fdt))
goto fail;
if ((fdt_size_dt_strings(fdt) - stroffset) < len)
len = fdt_size_dt_strings(fdt) - stroffset;
}
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
if ((stroffset >= 0)
|| (stroffset < -fdt_size_dt_strings(fdt)))
unsigned int sw_stroffset = -stroffset;
if ((stroffset >= 0) ||
(sw_stroffset > fdt_size_dt_strings(fdt)))
goto fail;
if ((-stroffset) < len)
len = -stroffset;
if (sw_stroffset < len)
len = sw_stroffset;
} else {
err = -FDT_ERR_INTERNAL;
goto fail;
@@ -157,8 +159,8 @@ int fdt_generate_phandle(const void *fdt, uint32_t *phandle)
static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n)
{
int offset = n * sizeof(struct fdt_reserve_entry);
int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
unsigned int offset = n * sizeof(struct fdt_reserve_entry);
unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
if (!can_assume(VALID_INPUT)) {
if (absoffset < fdt_off_mem_rsvmap(fdt))
@@ -179,8 +181,8 @@ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
if (!can_assume(VALID_INPUT) && !re)
return -FDT_ERR_BADOFFSET;
*address = fdt64_ld(&re->address);
*size = fdt64_ld(&re->size);
*address = fdt64_ld_(&re->address);
*size = fdt64_ld_(&re->size);
return 0;
}
@@ -190,7 +192,7 @@ int fdt_num_mem_rsv(const void *fdt)
const struct fdt_reserve_entry *re;
for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) {
if (fdt64_ld(&re->size) == 0)
if (fdt64_ld_(&re->size) == 0)
return i;
}
return -FDT_ERR_TRUNCATED;
@@ -368,7 +370,7 @@ static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt,
prop = fdt_offset_ptr_(fdt, offset);
if (lenp)
*lenp = fdt32_ld(&prop->len);
*lenp = fdt32_ld_(&prop->len);
return prop;
}
@@ -406,7 +408,7 @@ static const struct fdt_property *fdt_get_property_namelen_(const void *fdt,
offset = -FDT_ERR_INTERNAL;
break;
}
if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff),
if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff),
name, namelen)) {
if (poffset)
*poffset = offset;
@@ -459,7 +461,7 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
/* Handle realignment */
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
(poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
(poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
return prop->data + 4;
return prop->data;
}
@@ -477,7 +479,7 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
int namelen;
if (!can_assume(VALID_INPUT)) {
name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff),
name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff),
&namelen);
if (!name) {
if (lenp)
@@ -486,13 +488,13 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset,
}
*namep = name;
} else {
*namep = fdt_string(fdt, fdt32_ld(&prop->nameoff));
*namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff));
}
}
/* Handle realignment */
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 &&
(offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8)
(offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8)
return prop->data + 4;
return prop->data;
}
@@ -517,7 +519,7 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
return 0;
}
return fdt32_ld(php);
return fdt32_ld_(php);
}
const char *fdt_get_alias_namelen(const void *fdt,
@@ -680,7 +682,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
{
int offset;
if ((phandle == 0) || (phandle == -1))
if ((phandle == 0) || (phandle == ~0U))
return -FDT_ERR_BADPHANDLE;
FDT_RO_PROBE(fdt);

View File

@@ -46,7 +46,7 @@ static int fdt_rw_probe_(void *fdt)
return err_; \
}
static inline int fdt_data_size_(void *fdt)
static inline unsigned int fdt_data_size_(void *fdt)
{
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
}
@@ -54,15 +54,16 @@ static inline int fdt_data_size_(void *fdt)
static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
{
char *p = splicepoint;
char *end = (char *)fdt + fdt_data_size_(fdt);
unsigned int dsize = fdt_data_size_(fdt);
size_t soff = p - (char *)fdt;
if (((p + oldlen) < p) || ((p + oldlen) > end))
if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
return -FDT_ERR_BADOFFSET;
if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
return -FDT_ERR_BADOFFSET;
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
if (dsize - oldlen + newlen > fdt_totalsize(fdt))
return -FDT_ERR_NOSPACE;
memmove(p + newlen, p + oldlen, end - p - oldlen);
memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
return 0;
}
@@ -348,7 +349,10 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
return offset;
/* Try to place the new node after the parent's properties */
fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
/* the fdt_subnode_offset_namelen() should ensure this never hits */
if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
return -FDT_ERR_INTERNAL;
do {
offset = nextoffset;
tag = fdt_next_tag(fdt, offset, &nextoffset);
@@ -390,7 +394,9 @@ int fdt_del_node(void *fdt, int nodeoffset)
}
static void fdt_packblocks_(const char *old, char *new,
int mem_rsv_size, int struct_size)
int mem_rsv_size,
int struct_size,
int strings_size)
{
int mem_rsv_off, struct_off, strings_off;
@@ -405,8 +411,7 @@ static void fdt_packblocks_(const char *old, char *new,
fdt_set_off_dt_struct(new, struct_off);
fdt_set_size_dt_struct(new, struct_size);
memmove(new + strings_off, old + fdt_off_dt_strings(old),
fdt_size_dt_strings(old));
memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
fdt_set_off_dt_strings(new, strings_off);
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
}
@@ -427,15 +432,17 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
struct_size = fdt_size_dt_struct(fdt);
} else {
} else if (fdt_version(fdt) == 16) {
struct_size = 0;
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
;
if (struct_size < 0)
return struct_size;
} else {
return -FDT_ERR_BADVERSION;
}
if (can_assume(LIBFDT_ORDER) |
if (can_assume(LIBFDT_ORDER) ||
!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
/* no further work necessary */
err = fdt_move(fdt, buf, bufsize);
@@ -464,7 +471,8 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)
return -FDT_ERR_NOSPACE;
}
fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size);
fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
fdt_size_dt_strings(fdt));
memmove(buf, tmp, newsize);
fdt_set_magic(buf, FDT_MAGIC);
@@ -484,7 +492,8 @@ int fdt_pack(void *fdt)
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
* sizeof(struct fdt_reserve_entry);
fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
fdt_size_dt_strings(fdt));
fdt_set_totalsize(fdt, fdt_data_size_(fdt));
return 0;

View File

@@ -40,7 +40,7 @@ static struct fdt_errtabent fdt_errtable[] = {
FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
FDT_ERRTABENT(FDT_ERR_BADFLAGS),
};
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])))
const char *fdt_strerror(int errval)
{
@@ -48,7 +48,7 @@ const char *fdt_strerror(int errval)
return "<valid offset/length>";
else if (errval == 0)
return "<no error>";
else if (errval > -FDT_ERRTABSIZE) {
else if (-errval < FDT_ERRTABSIZE) {
const char *s = fdt_errtable[-errval].str;
if (s)

View File

@@ -32,7 +32,7 @@ static int fdt_sw_probe_(void *fdt)
/* 'memrsv' state: Initial state after fdt_create()
*
* Allowed functions:
* fdt_add_reservmap_entry()
* fdt_add_reservemap_entry()
* fdt_finish_reservemap() [moves to 'struct' state]
*/
static int fdt_sw_probe_memrsv_(void *fdt)
@@ -93,8 +93,8 @@ static inline uint32_t sw_flags(void *fdt)
static void *fdt_grab_space_(void *fdt, size_t len)
{
int offset = fdt_size_dt_struct(fdt);
int spaceleft;
unsigned int offset = fdt_size_dt_struct(fdt);
unsigned int spaceleft;
spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
- fdt_size_dt_strings(fdt);
@@ -108,8 +108,8 @@ static void *fdt_grab_space_(void *fdt, size_t len)
int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
{
const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
sizeof(struct fdt_reserve_entry));
const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
sizeof(struct fdt_reserve_entry));
void *fdt = buf;
if (bufsize < hdrsize)
@@ -152,6 +152,9 @@ int fdt_resize(void *fdt, void *buf, int bufsize)
FDT_SW_PROBE(fdt);
if (bufsize < 0)
return -FDT_ERR_NOSPACE;
headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
tailsize = fdt_size_dt_strings(fdt);
@@ -159,7 +162,7 @@ int fdt_resize(void *fdt, void *buf, int bufsize)
headsize + tailsize > fdt_totalsize(fdt))
return -FDT_ERR_INTERNAL;
if ((headsize + tailsize) > bufsize)
if ((headsize + tailsize) > (unsigned)bufsize)
return -FDT_ERR_NOSPACE;
oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
@@ -247,18 +250,18 @@ int fdt_end_node(void *fdt)
static int fdt_add_string_(void *fdt, const char *s)
{
char *strtab = (char *)fdt + fdt_totalsize(fdt);
int strtabsize = fdt_size_dt_strings(fdt);
int len = strlen(s) + 1;
int struct_top, offset;
unsigned int strtabsize = fdt_size_dt_strings(fdt);
unsigned int len = strlen(s) + 1;
unsigned int struct_top, offset;
offset = -strtabsize - len;
offset = strtabsize + len;
struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
if (fdt_totalsize(fdt) + offset < struct_top)
if (fdt_totalsize(fdt) - offset < struct_top)
return 0; /* no more room :( */
memcpy(strtab + offset, s, len);
memcpy(strtab - offset, s, len);
fdt_set_size_dt_strings(fdt, strtabsize + len);
return offset;
return -offset;
}
/* Must only be used to roll back in case of error */
@@ -374,7 +377,7 @@ int fdt_finish(void *fdt)
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
/* And fix up fields that were keeping intermediate state. */
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION);
fdt_set_magic(fdt, FDT_MAGIC);
return 0;

Some files were not shown because too many files have changed in this diff Show More