Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9218cf8bda | ||
|
|
a4e3aa4e1a | ||
|
|
a29e52d425 | ||
|
|
0d7c0f02c2 | ||
|
|
b50ce88239 | ||
|
|
c592b20a82 | ||
|
|
2cee0001cf | ||
|
|
9ff58710dc | ||
|
|
1efd4fac28 | ||
|
|
9a16d1b224 | ||
|
|
b623ef5316 | ||
|
|
62cae19be7 | ||
|
|
19a9358da7 | ||
|
|
94d7dbdb57 | ||
|
|
04a18b4691 | ||
|
|
52b9396b85 | ||
|
|
26fb52905a | ||
|
|
fbd054a4fc | ||
|
|
3f8c24809d | ||
|
|
a914ee741e | ||
|
|
287b4f33ba | ||
|
|
51aeda9707 | ||
|
|
d9aae4f4af | ||
|
|
7e6d4c7f01 | ||
|
|
6adb8f8d7c | ||
|
|
8ff7007198 | ||
|
|
eefd289876 | ||
|
|
51865fe6dd | ||
|
|
9e0bc7a888 | ||
|
|
923144e37a | ||
|
|
fd542ea941 | ||
|
|
97c5e54cae | ||
|
|
5e9f53bfd8 | ||
|
|
2c92a18892 | ||
|
|
5a756faaa0 | ||
|
|
fd56eb635b | ||
|
|
94c88d8121 | ||
|
|
c16e8a325a | ||
|
|
e89d8fa26c | ||
|
|
3011345486 | ||
|
|
97e57b6cbb | ||
|
|
1300c08302 | ||
|
|
1b255b1bd6 | ||
|
|
3cb4e8f8d0 | ||
|
|
d13b9ed88f | ||
|
|
3e98d59d18 | ||
|
|
0186293fa8 | ||
|
|
03b479e6a3 | ||
|
|
49c58b5f2e | ||
|
|
f36b77805e | ||
|
|
b2d53838fd | ||
|
|
b52f4ffdf3 | ||
|
|
5fb1e11d77 | ||
|
|
b9529e0774 | ||
|
|
2bb40f5dd2 | ||
|
|
c7ce40a7bd | ||
|
|
ee68a07899 | ||
|
|
d106e08273 | ||
|
|
5d280d6049 | ||
|
|
96da27fe3b |
No files matched your search
@@ -0,0 +1,15 @@
|
||||
name: PR
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
build:
|
||||
needs: [ changes ]
|
||||
if: ${{needs.changes.outputs.src == 'true' && !github.event.pull_request.draft}}
|
||||
uses: ./.github/workflows/build_linux.yml
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Ubuntu
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
default: ubuntu-latest
|
||||
type: string
|
||||
required: false
|
||||
|
||||
upload:
|
||||
default: false
|
||||
type: boolean
|
||||
required: false
|
||||
|
||||
ref:
|
||||
default: ${{ github.ref }}
|
||||
type: string
|
||||
required: false
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
os:
|
||||
description: Environment
|
||||
default: ubuntu-latest
|
||||
type: string
|
||||
required: true
|
||||
|
||||
upload:
|
||||
description: Upload
|
||||
default: true
|
||||
type: boolean
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ github.event.inputs.os || inputs.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{inputs.ref || github.ref}}
|
||||
|
||||
- name: Install Ubuntu dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install make ninja-build pkg-config libssl-dev libsdl2-dev libpixman-1-dev libpixman-1-0 libglib2.0-dev
|
||||
@@ -1,30 +0,0 @@
|
||||
# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown
|
||||
|
||||
name: 'Repo Lockdown'
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: opened
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/repo-lockdown@v2
|
||||
with:
|
||||
pr-comment: |
|
||||
Thank you for your interest in the QEMU project.
|
||||
|
||||
This repository is a read-only mirror of the project's repostories hosted
|
||||
on https://gitlab.com/qemu-project/qemu.git.
|
||||
The project does not process merge requests filed on GitHub.
|
||||
|
||||
QEMU welcomes contributions of code (either fixing bugs or adding new
|
||||
functionality). However, we get a lot of patches, and so we have some
|
||||
guidelines about contributing on the project website:
|
||||
https://www.qemu.org/contribute/
|
||||
lock-pr: true
|
||||
close-pr: true
|
||||
@@ -20,3 +20,5 @@ GTAGS
|
||||
*.swp
|
||||
*.patch
|
||||
*.gcov
|
||||
|
||||
*.DS_Store
|
||||
@@ -647,10 +647,7 @@ pages:
|
||||
- mkdir -p public
|
||||
# HTML-ised source tree
|
||||
- make gtags
|
||||
# We unset variables to work around a bug in some htags versions
|
||||
# which causes it to fail when the environment is large
|
||||
- CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags
|
||||
-anT --tree-view=filetree -m qemu_init
|
||||
- htags -anT --tree-view=filetree -m qemu_init
|
||||
-t "Welcome to the QEMU sourcecode"
|
||||
- mv HTML public/src
|
||||
# Project documentation
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
.cirrus_build_job:
|
||||
extends: .base_job_template
|
||||
stage: build
|
||||
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:latest
|
||||
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
|
||||
needs: []
|
||||
# 20 mins larger than "timeout_in" in cirrus/build.yml
|
||||
# as there's often a 5-10 minute delay before Cirrus CI
|
||||
@@ -52,12 +52,11 @@ x64-freebsd-13-build:
|
||||
NAME: freebsd-13
|
||||
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
|
||||
CIRRUS_VM_IMAGE_SELECTOR: image_family
|
||||
CIRRUS_VM_IMAGE_NAME: freebsd-13-3
|
||||
CIRRUS_VM_IMAGE_NAME: freebsd-13-2
|
||||
CIRRUS_VM_CPUS: 8
|
||||
CIRRUS_VM_RAM: 8G
|
||||
UPDATE_COMMAND: pkg update; pkg upgrade -y
|
||||
INSTALL_COMMAND: pkg install -y
|
||||
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu
|
||||
TEST_TARGETS: check
|
||||
|
||||
aarch64-macos-13-base-build:
|
||||
@@ -73,7 +72,6 @@ aarch64-macos-13-base-build:
|
||||
INSTALL_COMMAND: brew install
|
||||
PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin
|
||||
PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig
|
||||
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblazeel-softmmu,mips64-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4-softmmu,xtensaeb-softmmu
|
||||
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
|
||||
|
||||
aarch64-macos-14-base-build:
|
||||
|
||||
@@ -21,7 +21,7 @@ build_task:
|
||||
install_script:
|
||||
- @UPDATE_COMMAND@
|
||||
- @INSTALL_COMMAND@ @PKGS@
|
||||
- if test -n "@PYPI_PKGS@" ; then PYLIB=$(@PYTHON@ -c 'import sysconfig; print(sysconfig.get_path("stdlib"))'); rm -f $PYLIB/EXTERNALLY-MANAGED; @PIP3@ install @PYPI_PKGS@ ; fi
|
||||
- if test -n "@PYPI_PKGS@" ; then @PIP3@ install @PYPI_PKGS@ ; fi
|
||||
clone_script:
|
||||
- git clone --depth 100 "$CI_REPOSITORY_URL" .
|
||||
- git fetch origin "$CI_COMMIT_REF_NAME"
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
$MINGW_TARGET-libpng
|
||||
$MINGW_TARGET-libssh
|
||||
$MINGW_TARGET-libtasn1
|
||||
$MINGW_TARGET-libusb
|
||||
$MINGW_TARGET-lzo2
|
||||
$MINGW_TARGET-nettle
|
||||
$MINGW_TARGET-ninja
|
||||
@@ -97,8 +98,9 @@
|
||||
$MINGW_TARGET-SDL2
|
||||
$MINGW_TARGET-SDL2_image
|
||||
$MINGW_TARGET-snappy
|
||||
$MINGW_TARGET-zstd
|
||||
$EXTRA_PACKAGES "
|
||||
$MINGW_TARGET-spice
|
||||
$MINGW_TARGET-usbredir
|
||||
$MINGW_TARGET-zstd "
|
||||
- Write-Output "Running build at $(Get-Date -Format u)"
|
||||
- $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
|
||||
- $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink
|
||||
@@ -121,8 +123,6 @@ msys2-64bit:
|
||||
variables:
|
||||
MINGW_TARGET: mingw-w64-x86_64
|
||||
MSYSTEM: MINGW64
|
||||
# msys2 only ship these packages for 64-bit, not 32-bit
|
||||
EXTRA_PACKAGES: $MINGW_TARGET-libusb $MINGW_TARGET-usbredir $MINGW_TARGET-spice
|
||||
# do not remove "--without-default-devices"!
|
||||
# commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices"
|
||||
# changed to compile QEMU with the --without-default-devices switch
|
||||
@@ -131,3 +131,11 @@ msys2-64bit:
|
||||
# qTests don't run successfully with "--without-default-devices",
|
||||
# so let's exclude the qtests from CI for now.
|
||||
TEST_ARGS: --no-suite qtest
|
||||
|
||||
msys2-32bit:
|
||||
extends: .shared_msys2_builder
|
||||
variables:
|
||||
MINGW_TARGET: mingw-w64-i686
|
||||
MSYSTEM: MINGW32
|
||||
CONFIGURE_ARGS: --target-list=ppc64-softmmu -Ddebug=false -Doptimization=0
|
||||
TEST_ARGS: --no-suite qtest
|
||||
+7
-12
@@ -5,21 +5,16 @@
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# We recommend specifying your dependencies to enable reproducible builds:
|
||||
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
||||
# We want all the document formats
|
||||
formats: all
|
||||
|
||||
# For consistency, we require that QEMU's Sphinx extensions
|
||||
# run with at least the same minimum version of Python that
|
||||
# we require for other Python in our codebase (our conf.py
|
||||
# enforces this, and some code needs it.)
|
||||
python:
|
||||
version: 3.6
|
||||
@@ -0,0 +1,20 @@
|
||||
# QEMU-iOS
|
||||
|
||||
QEMU-iOS is an emulator for legacy Apple devices.
|
||||
Currently, the iPod Touch 1G and iPod Touch 2G are supported.
|
||||
|
||||
<img width="331" alt="it2g-qemu" src="https://github.com/devos50/qemu-ios/assets/1707075/9bf7f6c1-5918-47e9-bb3e-2e39ae15d519">
|
||||
|
||||
The schematic below shows the most important hardware components of the iPod Touch 2G and their interactions.
|
||||
The schematic for the iPod Touch 1G is mostly similar.
|
||||
|
||||
<img width="80%" alt="it2g-schematic" src="https://github.com/devos50/qemu-ios/assets/1707075/4b8eca9a-74b0-4590-ad23-bc056acde434">
|
||||
|
||||
### Running the iPod Touch 1G
|
||||
|
||||
Instructions on how to run the iPod Touch 1G emulator can be found [here](https://devos50.github.io/blog/2022/ipod-touch-qemu-pt2/).
|
||||
A technical blog post with more information about the peripherals and reverse engineering process is published [here](https://devos50.github.io/blog/2022/ipod-touch-qemu/).
|
||||
|
||||
### Running the iPod Touch 2G
|
||||
|
||||
Instructions on how to run the iPod Touch 2G emulator can be found [here](https://github.com/devos50/qemu-ios/blob/ipod_touch_2g/RUNNING.md).
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
===========
|
||||
QEMU README
|
||||
===========
|
||||
|
||||
QEMU is a generic and open source machine & userspace emulator and
|
||||
virtualizer.
|
||||
|
||||
QEMU is capable of emulating a complete machine in software without any
|
||||
need for hardware virtualization support. By using dynamic translation,
|
||||
it achieves very good performance. QEMU can also integrate with the Xen
|
||||
and KVM hypervisors to provide emulated hardware while allowing the
|
||||
hypervisor to manage the CPU. With hypervisor support, QEMU can achieve
|
||||
near native performance for CPUs. When QEMU emulates CPUs directly it is
|
||||
capable of running operating systems made for one machine (e.g. an ARMv7
|
||||
board) on a different machine (e.g. an x86_64 PC board).
|
||||
|
||||
QEMU is also capable of providing userspace API virtualization for Linux
|
||||
and BSD kernel interfaces. This allows binaries compiled against one
|
||||
architecture ABI (e.g. the Linux PPC64 ABI) to be run on a host using a
|
||||
different architecture ABI (e.g. the Linux x86_64 ABI). This does not
|
||||
involve any hardware emulation, simply CPU and syscall emulation.
|
||||
|
||||
QEMU aims to fit into a variety of use cases. It can be invoked directly
|
||||
by users wishing to have full control over its behaviour and settings.
|
||||
It also aims to facilitate integration into higher level management
|
||||
layers, by providing a stable command line interface and monitor API.
|
||||
It is commonly invoked indirectly via the libvirt library when using
|
||||
open source applications such as oVirt, OpenStack and virt-manager.
|
||||
|
||||
QEMU as a whole is released under the GNU General Public License,
|
||||
version 2. For full licensing details, consult the LICENSE file.
|
||||
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
Documentation can be found hosted online at
|
||||
`<https://www.qemu.org/documentation/>`_. The documentation for the
|
||||
current development version that is available at
|
||||
`<https://www.qemu.org/docs/master/>`_ is generated from the ``docs/``
|
||||
folder in the source tree, and is built by `Sphinx
|
||||
<https://www.sphinx-doc.org/en/master/>`_.
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
QEMU is multi-platform software intended to be buildable on all modern
|
||||
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
|
||||
of other UNIX targets. The simple steps to build QEMU are:
|
||||
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
../configure
|
||||
make
|
||||
|
||||
Additional information can also be found online via the QEMU website:
|
||||
|
||||
* `<https://wiki.qemu.org/Hosts/Linux>`_
|
||||
* `<https://wiki.qemu.org/Hosts/Mac>`_
|
||||
* `<https://wiki.qemu.org/Hosts/W32>`_
|
||||
|
||||
|
||||
Submitting patches
|
||||
==================
|
||||
|
||||
The QEMU source code is maintained under the GIT version control system.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://gitlab.com/qemu-project/qemu.git
|
||||
|
||||
When submitting patches, one common approach is to use 'git
|
||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||
qemu-devel@nongnu.org mailing list. All patches submitted must contain
|
||||
a 'Signed-off-by' line from the author. Patches should follow the
|
||||
guidelines set out in the `style section
|
||||
<https://www.qemu.org/docs/master/devel/style.html>`_ of
|
||||
the Developers Guide.
|
||||
|
||||
Additional information on submitting patches can be found online via
|
||||
the QEMU website
|
||||
|
||||
* `<https://wiki.qemu.org/Contribute/SubmitAPatch>`_
|
||||
* `<https://wiki.qemu.org/Contribute/TrivialPatches>`_
|
||||
|
||||
The QEMU website is also maintained under source control.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://gitlab.com/qemu-project/qemu-web.git
|
||||
|
||||
* `<https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/>`_
|
||||
|
||||
A 'git-publish' utility was created to make above process less
|
||||
cumbersome, and is highly recommended for making regular contributions,
|
||||
or even just for sending consecutive patch series revisions. It also
|
||||
requires a working 'git send-email' setup, and by default doesn't
|
||||
automate everything, so you may want to go through the above steps
|
||||
manually for once.
|
||||
|
||||
For installation instructions, please go to
|
||||
|
||||
* `<https://github.com/stefanha/git-publish>`_
|
||||
|
||||
The workflow with 'git-publish' is:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git checkout master -b my-feature
|
||||
$ # work on new commits, add your 'Signed-off-by' lines to each
|
||||
$ git publish
|
||||
|
||||
Your patch series will be sent and tagged as my-feature-v1 if you need to refer
|
||||
back to it in the future.
|
||||
|
||||
Sending v2:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ git checkout my-feature # same topic branch
|
||||
$ # making changes to the commits (using 'git rebase', for example)
|
||||
$ git publish
|
||||
|
||||
Your patch series will be sent with 'v2' tag in the subject and the git tip
|
||||
will be tagged as my-feature-v2.
|
||||
|
||||
Bug reporting
|
||||
=============
|
||||
|
||||
The QEMU project uses GitLab issues to track bugs. Bugs
|
||||
found when running code built from QEMU git or upstream released sources
|
||||
should be reported via:
|
||||
|
||||
* `<https://gitlab.com/qemu-project/qemu/-/issues>`_
|
||||
|
||||
If using QEMU via an operating system vendor pre-built binary package, it
|
||||
is preferable to report bugs to the vendor's own bug tracker first. If
|
||||
the bug is also known to affect latest upstream code, it can also be
|
||||
reported via GitLab.
|
||||
|
||||
For additional information on bug reporting consult:
|
||||
|
||||
* `<https://wiki.qemu.org/Contribute/ReportABug>`_
|
||||
|
||||
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
For version history and release notes, please visit
|
||||
`<https://wiki.qemu.org/ChangeLog/>`_ or look at the git history for
|
||||
more detailed information.
|
||||
|
||||
|
||||
Contact
|
||||
=======
|
||||
|
||||
The QEMU community can be contacted in a number of ways, with the two
|
||||
main methods being email and IRC
|
||||
|
||||
* `<mailto:qemu-devel@nongnu.org>`_
|
||||
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_
|
||||
* #qemu on irc.oftc.net
|
||||
|
||||
Information on additional methods of contacting the community can be
|
||||
found online via the QEMU website:
|
||||
|
||||
* `<https://wiki.qemu.org/Contribute/StartHere>`_
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
## Running the iPod Touch 2G Emulator
|
||||
|
||||
This file contains the instructions on how to run the iPod Touch 2G emulator using QEMU.
|
||||
Note that this is an experimental release and the functionality of the device is still limited.
|
||||
|
||||
### Building QEMU
|
||||
|
||||
Make sure you have the required libraries installed to compile QEMU:
|
||||
|
||||
```
|
||||
# On MacOS
|
||||
brew install glib ninja pixman pkg-config sdl2
|
||||
|
||||
# On Linux (Ubuntu)
|
||||
sudo apt install make ninja-build pkg-config libssl-dev libsdl2-dev libpixman-1-dev libpixman-1-0 libglib2.0-dev
|
||||
|
||||
# On Windows (MSYS2/mingw64)
|
||||
pacman -S base-devel mingw-w64-x86_64-toolchain git python ninja mingw-w64-x86_64-glib2 mingw-w64-x86_64-pixman python-setuptools mingw-w64-x86_64-SDL2
|
||||
|
||||
```
|
||||
Compile QEMU by running the following commands from the root directory:
|
||||
|
||||
```
|
||||
mkdir build
|
||||
cd build
|
||||
# On Intel Macs
|
||||
../configure --target-list=arm-softmmu --extra-cflags=-I/usr/local/opt/openssl@3/include --extra-ldflags='-L/usr/local/opt/openssl@3/lib -lcrypto'
|
||||
|
||||
# On Apple Silicon Macs
|
||||
../configure --enable-sdl --disable-cocoa --target-list=arm-softmmu --disable-capstone --disable-pie --disable-slirp --extra-cflags=-I/opt/homebrew/opt/openssl@3/include --extra-ldflags='-L/opt/homebrew/opt/openssl@3/lib -lcrypto'
|
||||
|
||||
# On Microsoft Windows (with MINGW64)
|
||||
../configure --enable-sdl --disable-cocoa --target-list=arm-softmmu --disable-capstone --disable-slirp --disable-pie --extra-cflags=-I/mingw64/include/openssl --extra-ldflags='-L/mingw64/lib -lcrypto' --disable-stack-protector --disable-werror
|
||||
|
||||
make
|
||||
```
|
||||
|
||||
Note that we’re explicitly enabling compilation of the SDL library which is used for interaction with the emulator (e.g., capturing keyboard and mouse events). Also, we only configure and build the ARM emulator.
|
||||
We are also linking against OpenSSL as the AES/SHA1/PKE engines use some of the library’s cryptographic functions.
|
||||
Remember to update the include and library paths to the OpenSSL library in case they are located elsewhere.
|
||||
You can speed up the `make` command by passing the number of available CPU cores with the `-j` flag, e.g., use `make -j6` to compile using six CPU cores.
|
||||
The compilation process should produce the `qemu-system-arm` binary in the `build/arm-softmmu` directory.
|
||||
|
||||
### Downloading the Required Files
|
||||
|
||||
We need a few files to successfully boot the iPod Touch emulator to the home screen, which I published [here](https://github.com/devos50/qemu-ios/releases/tag/n72ap_v1) for convenience. You can download all these files from here, and they include the following:
|
||||
- The S5L8720 bootrom binary, which is started as the very first thing when booting.
|
||||
- A NOR image that contains various auxillary files used by the bootloader. I provide some instructions on how to generate this image yourself [here](https://github.com/devos50/qemu-ios-generate-nor).
|
||||
- A NAND image that contains the root file system. I provide some instructions on how to generate this image yourself [here](https://github.com/devos50/qemu-ios-generate-nand).
|
||||
|
||||
Download all the required files and save them to a convenient location. You should unzip the `nand_n72ap.zip` file, which contains a single directory named `nand`.
|
||||
|
||||
### Running the Emulator
|
||||
|
||||
We are now ready to run the emulator from the build directory with the following command:
|
||||
|
||||
```
|
||||
./arm-softmmu/qemu-system-arm -M iPod-Touch,bootrom=<path to bootrom>,nand=<path to NAND directory>,nor=<path to NOR directory> -serial mon:stdio -cpu max -m 2G -d unimp
|
||||
```
|
||||
|
||||
If there are any issues running the above commands, please let me know by [opening an issue](https://github.com/devos50/qemu-ios/issues/new).
|
||||
@@ -183,7 +183,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
||||
const TranslationBlock *tb = p;
|
||||
const struct tb_desc *desc = d;
|
||||
|
||||
if (tb->pc == desc->pc &&
|
||||
if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) &&
|
||||
tb_page_addr0(tb) == desc->page_addr0 &&
|
||||
tb->cs_base == desc->cs_base &&
|
||||
tb->flags == desc->flags &&
|
||||
@@ -233,7 +233,7 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc,
|
||||
return NULL;
|
||||
}
|
||||
desc.page_addr0 = phys_pc;
|
||||
h = tb_hash_func(phys_pc, pc,
|
||||
h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc),
|
||||
flags, cs_base, cflags);
|
||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ static bool tb_cmp(const void *ap, const void *bp)
|
||||
const TranslationBlock *a = ap;
|
||||
const TranslationBlock *b = bp;
|
||||
|
||||
return (a->pc == b->pc &&
|
||||
return ((tb_cflags(a) & CF_PCREL || a->pc == b->pc) &&
|
||||
a->cs_base == b->cs_base &&
|
||||
a->flags == b->flags &&
|
||||
(tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) &&
|
||||
@@ -916,7 +916,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
|
||||
|
||||
/* remove the TB from the hash list */
|
||||
phys_pc = tb_page_addr0(tb);
|
||||
h = tb_hash_func(phys_pc, tb->pc,
|
||||
h = tb_hash_func(phys_pc, (orig_cflags & CF_PCREL ? 0 : tb->pc),
|
||||
tb->flags, tb->cs_base, orig_cflags);
|
||||
if (!qht_remove(&tb_ctx.htable, tb, h)) {
|
||||
return;
|
||||
@@ -983,7 +983,7 @@ TranslationBlock *tb_link_page(TranslationBlock *tb)
|
||||
tb_record(tb);
|
||||
|
||||
/* add in the hash table */
|
||||
h = tb_hash_func(tb_page_addr0(tb), tb->pc,
|
||||
h = tb_hash_func(tb_page_addr0(tb), (tb->cflags & CF_PCREL ? 0 : tb->pc),
|
||||
tb->flags, tb->cs_base, tb->cflags);
|
||||
qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
|
||||
|
||||
|
||||
@@ -327,7 +327,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
gen_code_buf = tcg_ctx->code_gen_ptr;
|
||||
tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
|
||||
tb->pc = pc;
|
||||
if (!(cflags & CF_PCREL)) {
|
||||
tb->pc = pc;
|
||||
}
|
||||
tb->cs_base = cs_base;
|
||||
tb->flags = flags;
|
||||
tb->cflags = cflags;
|
||||
|
||||
+1
-1
@@ -1744,7 +1744,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp)
|
||||
if (driver) {
|
||||
done = !audio_driver_init(s, driver, dev, errp);
|
||||
} else {
|
||||
error_setg(errp, "Unknown audio driver `%s'", drvname);
|
||||
error_setg(errp, "Unknown audio driver `%s'\n", drvname);
|
||||
}
|
||||
if (!done) {
|
||||
goto out;
|
||||
|
||||
+1
-2
@@ -30,8 +30,7 @@ endforeach
|
||||
|
||||
if dbus_display
|
||||
module_ss = ss.source_set()
|
||||
module_ss.add(when: [gio, dbus_display1_dep, pixman],
|
||||
if_true: files('dbusaudio.c'))
|
||||
module_ss.add(when: [gio, pixman], if_true: files('dbusaudio.c'))
|
||||
audio_modules += {'dbus': module_ss}
|
||||
endif
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/cryptodev.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "standard-headers/linux/virtio_crypto.h"
|
||||
#include "crypto/cipher.h"
|
||||
@@ -397,8 +396,8 @@ static int cryptodev_builtin_create_session(
|
||||
case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
|
||||
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
|
||||
default:
|
||||
error_report("Unsupported opcode :%" PRIu32 "",
|
||||
sess_info->op_code);
|
||||
error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
|
||||
sess_info->op_code);
|
||||
return -VIRTIO_CRYPTO_NOTSUPP;
|
||||
}
|
||||
|
||||
@@ -428,9 +427,7 @@ static int cryptodev_builtin_close_session(
|
||||
CRYPTODEV_BACKEND_BUILTIN(backend);
|
||||
CryptoDevBackendBuiltinSession *session;
|
||||
|
||||
if (session_id >= MAX_NUM_SESSIONS || !builtin->sessions[session_id]) {
|
||||
return -VIRTIO_CRYPTO_INVSESS;
|
||||
}
|
||||
assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);
|
||||
|
||||
session = builtin->sessions[session_id];
|
||||
if (session->cipher) {
|
||||
@@ -555,8 +552,8 @@ static int cryptodev_builtin_operation(
|
||||
|
||||
if (op_info->session_id >= MAX_NUM_SESSIONS ||
|
||||
builtin->sessions[op_info->session_id] == NULL) {
|
||||
error_report("Cannot find a valid session id: %" PRIu64 "",
|
||||
op_info->session_id);
|
||||
error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
|
||||
op_info->session_id);
|
||||
return -VIRTIO_CRYPTO_INVSESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -398,7 +398,6 @@ static void cryptodev_backend_set_ops(Object *obj, Visitor *v,
|
||||
static void
|
||||
cryptodev_backend_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc);
|
||||
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc);
|
||||
uint32_t services;
|
||||
@@ -407,20 +406,11 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp)
|
||||
QTAILQ_INIT(&backend->opinfos);
|
||||
value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg;
|
||||
cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||
cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bc->init) {
|
||||
bc->init(backend, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
services = backend->conf.crypto_services;
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ typedef struct {
|
||||
CoQueue bounce_available;
|
||||
|
||||
/* The value of the "mem-region-alignment" property */
|
||||
uint64_t mem_region_alignment;
|
||||
size_t mem_region_alignment;
|
||||
|
||||
/* Can we skip adding/deleting blkio_mem_regions? */
|
||||
bool needs_mem_regions;
|
||||
|
||||
+8
-25
@@ -328,39 +328,22 @@ static void coroutine_fn GRAPH_RDLOCK
|
||||
blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
|
||||
{
|
||||
BDRVBlkLogWritesState *s = lr->bs->opaque;
|
||||
|
||||
/*
|
||||
* Determine the offsets and sizes of different parts of the entry, and
|
||||
* update the state of the driver.
|
||||
*
|
||||
* This needs to be done in one go, before any actual I/O is done, as the
|
||||
* log entry may have to be written in two parts, and the state of the
|
||||
* driver may be modified by other driver operations while waiting for the
|
||||
* I/O to complete.
|
||||
*/
|
||||
const uint64_t entry_start_sector = s->cur_log_sector;
|
||||
const uint64_t entry_offset = entry_start_sector << s->sectorbits;
|
||||
const uint64_t qiov_aligned_size = ROUND_UP(lr->qiov->size, s->sectorsize);
|
||||
const uint64_t entry_aligned_size = qiov_aligned_size +
|
||||
ROUND_UP(lr->zero_size, s->sectorsize);
|
||||
const uint64_t entry_nr_sectors = entry_aligned_size >> s->sectorbits;
|
||||
uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
|
||||
|
||||
s->nr_entries++;
|
||||
s->cur_log_sector += entry_nr_sectors;
|
||||
s->cur_log_sector +=
|
||||
ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
|
||||
|
||||
/*
|
||||
* Write the log entry. Note that if this is a "write zeroes" operation,
|
||||
* only the entry header is written here, with the zeroing being done
|
||||
* separately below.
|
||||
*/
|
||||
lr->log_ret = bdrv_co_pwritev(s->log_file, entry_offset, lr->qiov->size,
|
||||
lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
|
||||
lr->qiov, 0);
|
||||
|
||||
/* Logging for the "write zeroes" operation */
|
||||
if (lr->log_ret == 0 && lr->zero_size) {
|
||||
const uint64_t zeroes_offset = entry_offset + qiov_aligned_size;
|
||||
cur_log_offset = s->cur_log_sector << s->sectorbits;
|
||||
s->cur_log_sector +=
|
||||
ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
|
||||
|
||||
lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, zeroes_offset,
|
||||
lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
|
||||
lr->zero_size, 0);
|
||||
}
|
||||
|
||||
|
||||
+11
-7
@@ -613,14 +613,14 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it)
|
||||
/* Must be called from the main loop */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
|
||||
old_bs = it->bs;
|
||||
|
||||
/* First, return all root nodes of BlockBackends. In order to avoid
|
||||
* returning a BDS twice when multiple BBs refer to it, we only return it
|
||||
* if the BB is the first one in the parent list of the BDS. */
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
|
||||
BlockBackend *old_blk = it->blk;
|
||||
|
||||
old_bs = old_blk ? blk_bs(old_blk) : NULL;
|
||||
|
||||
do {
|
||||
it->blk = blk_all_next(it->blk);
|
||||
bs = it->blk ? blk_bs(it->blk) : NULL;
|
||||
@@ -634,10 +634,11 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it)
|
||||
if (bs) {
|
||||
bdrv_ref(bs);
|
||||
bdrv_unref(old_bs);
|
||||
it->bs = bs;
|
||||
return bs;
|
||||
}
|
||||
it->phase = BDRV_NEXT_MONITOR_OWNED;
|
||||
} else {
|
||||
old_bs = it->bs;
|
||||
}
|
||||
|
||||
/* Then return the monitor-owned BDSes without a BB attached. Ignore all
|
||||
@@ -677,10 +678,13 @@ void bdrv_next_cleanup(BdrvNextIterator *it)
|
||||
/* Must be called from the main loop */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
|
||||
bdrv_unref(it->bs);
|
||||
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS && it->blk) {
|
||||
blk_unref(it->blk);
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
|
||||
if (it->blk) {
|
||||
bdrv_unref(blk_bs(it->blk));
|
||||
blk_unref(it->blk);
|
||||
}
|
||||
} else {
|
||||
bdrv_unref(it->bs);
|
||||
}
|
||||
|
||||
bdrv_next_reset(it);
|
||||
|
||||
+12
-29
@@ -1756,29 +1756,22 @@ static int bdrv_pad_request(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For prefetching in stream_populate(), no qiov is passed along, because
|
||||
* only copy-on-read matters.
|
||||
*/
|
||||
if (qiov && *qiov) {
|
||||
sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes,
|
||||
&sliced_head, &sliced_tail,
|
||||
&sliced_niov);
|
||||
sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes,
|
||||
&sliced_head, &sliced_tail,
|
||||
&sliced_niov);
|
||||
|
||||
/* Guaranteed by bdrv_check_request32() */
|
||||
assert(*bytes <= SIZE_MAX);
|
||||
ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov,
|
||||
sliced_head, *bytes);
|
||||
if (ret < 0) {
|
||||
bdrv_padding_finalize(pad);
|
||||
return ret;
|
||||
}
|
||||
*qiov = &pad->local_qiov;
|
||||
*qiov_offset = 0;
|
||||
/* Guaranteed by bdrv_check_request32() */
|
||||
assert(*bytes <= SIZE_MAX);
|
||||
ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov,
|
||||
sliced_head, *bytes);
|
||||
if (ret < 0) {
|
||||
bdrv_padding_finalize(pad);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*bytes += pad->head + pad->tail;
|
||||
*offset -= pad->head;
|
||||
*qiov = &pad->local_qiov;
|
||||
*qiov_offset = 0;
|
||||
if (padded) {
|
||||
*padded = true;
|
||||
}
|
||||
@@ -2626,16 +2619,6 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero,
|
||||
ret |= (ret2 & BDRV_BLOCK_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that the recursive search was done, clear the flag. Otherwise,
|
||||
* with more complicated block graphs like snapshot-access ->
|
||||
* copy-before-write -> qcow2, where the return value will be propagated
|
||||
* further up to a parent bdrv_co_do_block_status() call, both the
|
||||
* BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is
|
||||
* not allowed.
|
||||
*/
|
||||
ret &= ~BDRV_BLOCK_RECURSE;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
+4
-6
@@ -479,9 +479,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
|
||||
return bytes_handled;
|
||||
}
|
||||
|
||||
static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
|
||||
static void coroutine_fn GRAPH_RDLOCK mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source;
|
||||
BlockDriverState *source = s->mirror_top_bs->backing->bs;
|
||||
MirrorOp *pseudo_op;
|
||||
int64_t offset;
|
||||
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||
@@ -489,10 +489,6 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
|
||||
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
|
||||
int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES);
|
||||
|
||||
bdrv_graph_co_rdlock();
|
||||
source = s->mirror_top_bs->backing->bs;
|
||||
bdrv_graph_co_rdunlock();
|
||||
|
||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||
offset = bdrv_dirty_iter_next(s->dbi);
|
||||
if (offset < 0) {
|
||||
@@ -1082,7 +1078,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
|
||||
mirror_wait_for_free_in_flight_slot(s);
|
||||
continue;
|
||||
} else if (cnt != 0) {
|
||||
bdrv_graph_co_rdlock();
|
||||
mirror_iteration(s);
|
||||
bdrv_graph_co_rdunlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -196,10 +196,8 @@ bdrv_snapshot_fallback(BlockDriverState *bs)
|
||||
int bdrv_can_snapshot(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
|
||||
GLOBAL_STATE_CODE();
|
||||
|
||||
if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) {
|
||||
if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -518,7 +518,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp)
|
||||
|
||||
if (object_class_is_abstract(oc)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
|
||||
"a non-abstract device type");
|
||||
"an abstract device type");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ include ../arm-softmmu/default.mak
|
||||
# CONFIG_XLNX_ZYNQMP_ARM=n
|
||||
# CONFIG_XLNX_VERSAL=n
|
||||
# CONFIG_SBSA_REF=n
|
||||
CONFIG_ATV4=y
|
||||
@@ -44,3 +44,4 @@ CONFIG_ARM_VIRT=y
|
||||
# CONFIG_FSL_IMX7=n
|
||||
# CONFIG_FSL_IMX6UL=n
|
||||
# CONFIG_ALLWINNER_H3=n
|
||||
CONFIG_IPOD_TOUCH_2G=y
|
||||
@@ -1387,8 +1387,8 @@ probe_target_compiler() {
|
||||
done
|
||||
|
||||
try=cross
|
||||
# For softmmu/roms also look for a bi-endian or multilib-enabled host compiler
|
||||
if [ "${1%softmmu}" != "$1" ] || test "$target_arch" = "$cpu"; then
|
||||
# For softmmu/roms we might be able to use the host compiler
|
||||
if [ "${1%softmmu}" != "$1" ]; then
|
||||
case "$target_arch:$cpu" in
|
||||
aarch64_be:aarch64 | \
|
||||
armeb:arm | \
|
||||
@@ -1675,9 +1675,6 @@ fi
|
||||
mkdir -p tests/tcg
|
||||
echo "# Automatically generated by configure - do not modify" > $config_host_mak
|
||||
echo "SRC_PATH=$source_path" >> $config_host_mak
|
||||
if test "$plugins" = "yes" ; then
|
||||
echo "CONFIG_PLUGIN=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
tcg_tests_targets=
|
||||
for target in $target_list; do
|
||||
|
||||
@@ -327,7 +327,7 @@ virgl_get_resource_info_modifiers(uint32_t resource_id,
|
||||
#ifdef VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION
|
||||
struct virgl_renderer_resource_info_ext info_ext;
|
||||
ret = virgl_renderer_resource_get_info_ext(resource_id, &info_ext);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ virgl_get_resource_info_modifiers(uint32_t resource_id,
|
||||
*modifiers = info_ext.modifiers;
|
||||
#else
|
||||
ret = virgl_renderer_resource_get_info(resource_id, info);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ virgl_cmd_set_scanout(VuGpu *g,
|
||||
uint64_t modifiers = 0;
|
||||
ret = virgl_get_resource_info_modifiers(ss.resource_id, &info,
|
||||
&modifiers);
|
||||
if (ret) {
|
||||
if (ret == -1) {
|
||||
g_critical("%s: illegal resource specified %d\n",
|
||||
__func__, ss.resource_id);
|
||||
cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
|
||||
|
||||
+6
-4
@@ -29,6 +29,7 @@
|
||||
import os
|
||||
import sys
|
||||
import sphinx
|
||||
from distutils.version import LooseVersion
|
||||
from sphinx.errors import ConfigError
|
||||
|
||||
# The per-manual conf.py will set qemu_docdir for a single-manual build;
|
||||
@@ -164,10 +165,11 @@ html_theme = 'sphinx_rtd_theme'
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {
|
||||
"style_nav_header_background": "#802400",
|
||||
"navigation_with_keys": True,
|
||||
}
|
||||
if LooseVersion(sphinx_rtd_theme.__version__) >= LooseVersion("0.4.3"):
|
||||
html_theme_options = {
|
||||
"style_nav_header_background": "#802400",
|
||||
"navigation_with_keys": True,
|
||||
}
|
||||
|
||||
html_logo = os.path.join(qemu_docdir, "../ui/icons/qemu_128x128.png")
|
||||
|
||||
|
||||
@@ -148,9 +148,9 @@ Vring descriptor indices for packed virtqueues
|
||||
A vring address description
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
+-------+-------+------------+------+-----------+-----+
|
||||
| index | flags | descriptor | used | available | log |
|
||||
+-------+-------+------------+------+-----------+-----+
|
||||
+-------+-------+------+------------+------+-----------+-----+
|
||||
| index | flags | size | descriptor | used | available | log |
|
||||
+-------+-------+------+------------+------+-----------+-----+
|
||||
|
||||
:index: a 32-bit vring index
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
sphinx==5.3.0
|
||||
sphinx_rtd_theme==1.1.1
|
||||
@@ -1,9 +1,8 @@
|
||||
During the graphical emulation, you can use special key combinations from
|
||||
the following table to change modes. By default the modifier is Ctrl-Alt
|
||||
(used in the table below) which can be changed with ``-display`` suboption
|
||||
``mod=`` where appropriate. For example, ``-display sdl,
|
||||
grab-mod=lshift-lctrl-lalt`` changes the modifier key to Ctrl-Alt-Shift,
|
||||
while ``-display sdl,grab-mod=rctrl`` changes it to the right Ctrl key.
|
||||
During the graphical emulation, you can use special key combinations to
|
||||
change modes. The default key mappings are shown below, but if you use
|
||||
``-alt-grab`` then the modifier is Ctrl-Alt-Shift (instead of Ctrl-Alt)
|
||||
and if you use ``-ctrl-grab`` then the modifier is the right Ctrl key
|
||||
(instead of Ctrl-Alt):
|
||||
|
||||
Ctrl-Alt-f
|
||||
Toggle full screen
|
||||
@@ -29,7 +28,7 @@ Ctrl-Alt-n
|
||||
*3*
|
||||
Serial port
|
||||
|
||||
Ctrl-Alt-g
|
||||
Ctrl-Alt
|
||||
Toggle mouse and keyboard grab.
|
||||
|
||||
In the virtual consoles, you can use Ctrl-Up, Ctrl-Down, Ctrl-PageUp and
|
||||
|
||||
+1
-5
@@ -78,7 +78,6 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb,
|
||||
uint32_t *initiator_list)
|
||||
{
|
||||
int i, index;
|
||||
uint32_t initiator_to_index[MAX_NODES] = {};
|
||||
HMAT_LB_Data *lb_data;
|
||||
uint16_t *entry_list;
|
||||
uint32_t base;
|
||||
@@ -122,8 +121,6 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb,
|
||||
/* Initiator Proximity Domain List */
|
||||
for (i = 0; i < num_initiator; i++) {
|
||||
build_append_int_noprefix(table_data, initiator_list[i], 4);
|
||||
/* Reverse mapping for array possitions */
|
||||
initiator_to_index[initiator_list[i]] = i;
|
||||
}
|
||||
|
||||
/* Target Proximity Domain List */
|
||||
@@ -135,8 +132,7 @@ static void build_hmat_lb(GArray *table_data, HMAT_LB_Info *hmat_lb,
|
||||
entry_list = g_new0(uint16_t, num_initiator * num_target);
|
||||
for (i = 0; i < hmat_lb->list->len; i++) {
|
||||
lb_data = &g_array_index(hmat_lb->list, HMAT_LB_Data, i);
|
||||
index = initiator_to_index[lb_data->initiator] * num_target +
|
||||
lb_data->target;
|
||||
index = lb_data->initiator * num_target + lb_data->target;
|
||||
|
||||
entry_list[index] = (uint16_t)(lb_data->data / hmat_lb->base);
|
||||
}
|
||||
|
||||
+3
-2
@@ -680,6 +680,7 @@ config ARMSSE
|
||||
select SSE_COUNTER
|
||||
select SSE_TIMER
|
||||
|
||||
config ATV4
|
||||
config IPOD_TOUCH_2G
|
||||
bool
|
||||
select ATV4
|
||||
select IPOD_TOUCH_2G
|
||||
select PL192
|
||||
-198
@@ -1,198 +0,0 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "target/arm/cpu.h"
|
||||
#include "target/arm/cpregs.h"
|
||||
#include "hw/arm/atv4.h"
|
||||
|
||||
#define VMSTATE_ATV4_CPREG(name) \
|
||||
VMSTATE_UINT64(ATV4_CPREG_VAR_NAME(name), ATV4MachineState)
|
||||
|
||||
#define ATV4_CPREG_DEF(p_name, p_op0, p_op1, p_crn, p_crm, p_op2, p_access, p_reset) \
|
||||
{ \
|
||||
.cp = CP_REG_ARM64_SYSREG_CP, \
|
||||
.name = #p_name, .opc0 = p_op0, .crn = p_crn, .crm = p_crm, \
|
||||
.opc1 = p_op1, .opc2 = p_op2, .access = p_access, .resetvalue = p_reset, \
|
||||
.state = ARM_CP_STATE_AA64, .type = ARM_CP_OVERRIDE, \
|
||||
.fieldoffset = 5 \
|
||||
}
|
||||
|
||||
static void allocate_ram(MemoryRegion *top, const char *name, uint64_t addr, uint32_t size)
|
||||
{
|
||||
MemoryRegion *sec = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(sec, NULL, name, size, &error_fatal);
|
||||
memory_region_add_subregion(top, addr, sec);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo atv4_cp_reginfo_tcg[] = {
|
||||
ATV4_CPREG_DEF(REG0, 3, 0, 15, 1, 0, PL1_RW, 0),
|
||||
ATV4_CPREG_DEF(REG1, 3, 0, 15, 5, 0, PL1_RW, 0),
|
||||
ATV4_CPREG_DEF(REG2, 3, 6, 12, 0, 0, PL1_RW, 0),
|
||||
ATV4_CPREG_DEF(REG3, 3, 6, 1, 1, 0, PL1_RW, 0),
|
||||
ATV4_CPREG_DEF(REG4, 3, 6, 1, 0, 0, PL1_RW, 0),
|
||||
};
|
||||
|
||||
static void atv4_cpu_setup(MachineState *machine, MemoryRegion **sysmem, ARMCPU **cpu, AddressSpace **nsas)
|
||||
{
|
||||
Object *cpuobj = object_new(machine->cpu_type);
|
||||
*cpu = ARM_CPU(cpuobj);
|
||||
CPUState *cs = CPU(*cpu);
|
||||
|
||||
*sysmem = get_system_memory();
|
||||
|
||||
object_property_set_link(cpuobj, "memory", OBJECT(*sysmem), &error_abort);
|
||||
|
||||
object_property_set_bool(cpuobj, "realized", true, &error_fatal);
|
||||
|
||||
*nsas = cpu_get_address_space(cs, ARMASIdx_NS);
|
||||
|
||||
define_arm_cp_regs(*cpu, atv4_cp_reginfo_tcg);
|
||||
|
||||
object_unref(cpuobj);
|
||||
}
|
||||
|
||||
static void atv4_cpu_reset(void *opaque)
|
||||
{
|
||||
ATV4MachineState *nms = ATV4_MACHINE((MachineState *)opaque);
|
||||
ARMCPU *cpu = nms->cpu;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
cpu_reset(cs);
|
||||
|
||||
cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
|
||||
}
|
||||
|
||||
static void atv4_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
|
||||
{
|
||||
// ATV4MachineState *nms = ATV4_MACHINE(machine);
|
||||
allocate_ram(sysmem, "sram", SRAM_MEM_BASE, SRAM_LENGTH);
|
||||
allocate_ram(sysmem, "unknown2", UNKNOWN2_MEM_BASE, 0x1000);
|
||||
allocate_ram(sysmem, "unknown3", UNKNOWN3_MEM_BASE, 0x20);
|
||||
|
||||
// load the bootrom (vrom)
|
||||
uint8_t *file_data = NULL;
|
||||
unsigned long fsize;
|
||||
if (g_file_get_contents("/Users/martijndevos/Downloads/atv4_securerom", (char **)&file_data, &fsize, NULL)) {
|
||||
allocate_ram(sysmem, "vrom", VROM_MEM_BASE, 0x200000);
|
||||
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void atv4_machine_init(MachineState *machine)
|
||||
{
|
||||
ATV4MachineState *nms = ATV4_MACHINE(machine);
|
||||
MemoryRegion *sysmem;
|
||||
AddressSpace *nsas;
|
||||
ARMCPU *cpu;
|
||||
|
||||
atv4_cpu_setup(machine, &sysmem, &cpu, &nsas);
|
||||
|
||||
nms->cpu = cpu;
|
||||
nms->nsas = nsas;
|
||||
|
||||
atv4_memory_setup(machine, sysmem, nsas);
|
||||
|
||||
// init the interrupt controller
|
||||
AppleAICState *aic_state = apple_aic_create();
|
||||
nms->aic_state = aic_state;
|
||||
SysBusDevice *busdev = SYS_BUS_DEVICE(nms->aic_state);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
for(int i = 0; i < nms->aic_state->numCPU; i++) {
|
||||
memory_region_add_subregion(sysmem, AIC_MEM_BASE + i * 0x1000, &aic_state->cpus[i].iomem);
|
||||
}
|
||||
|
||||
// init the chip ID module
|
||||
DeviceState *dev = qdev_new("atv4.chipid");
|
||||
ATV4ChipIDState *chipid_state = ATV4_CHIPID(dev);
|
||||
nms->chipid_state = chipid_state;
|
||||
memory_region_add_subregion(sysmem, CHIPID_MEM_BASE, &chipid_state->iomem);
|
||||
|
||||
// init the PMGR module
|
||||
dev = qdev_new("atv4.pmgr");
|
||||
ATV4PMGRState *pmgr_state = ATV4_PMGR(dev);
|
||||
nms->pmgr_state = pmgr_state;
|
||||
memory_region_add_subregion(sysmem, PMGR_PLL_MEM_BASE, &pmgr_state->pll_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_VOLMAN_MEM_BASE, &pmgr_state->volman_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_PS_MEM_BASE, &pmgr_state->ps_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_GFX_MEM_BASE, &pmgr_state->gfx_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_ACG_MEM_BASE, &pmgr_state->acg_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_CLK_MEM_BASE, &pmgr_state->clk_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_CLKCFG_MEM_BASE, &pmgr_state->clkcfg_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_SOC_MEM_BASE, &pmgr_state->soc_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_SCRATCH_MEM_BASE, &pmgr_state->scratch_iomem);
|
||||
memory_region_add_subregion(sysmem, PMGR_PWRGATE_MEM_BASE, &pmgr_state->pwrgate_iomem);
|
||||
|
||||
// init GPIO
|
||||
dev = qdev_new("atv4.gpio");
|
||||
ATV4GPIOState *gpio_state = ATV4_GPIO(dev);
|
||||
nms->gpio_state = gpio_state;
|
||||
memory_region_add_subregion(sysmem, GPIO_MEM_BASE, &gpio_state->iomem);
|
||||
|
||||
// init the Apple USB
|
||||
dev = qdev_new("atv4.ausb");
|
||||
ATV4AUSBState *ausb_state = ATV4_AUSB(dev);
|
||||
nms->ausb_state = ausb_state;
|
||||
memory_region_add_subregion(sysmem, AUSB_MEM_BASE, &ausb_state->iomem);
|
||||
|
||||
// init the AUSB Widget
|
||||
dev = qdev_new("atv4.ausbwidget");
|
||||
ATV4AUSBWidgetState *ausb_widget_state = ATV4_AUSB_WIDGET(dev);
|
||||
nms->ausb_widget_state = ausb_widget_state;
|
||||
memory_region_add_subregion(sysmem, AUSB_WIDGET_MEM_BASE, &ausb_widget_state->iomem);
|
||||
|
||||
// init the ANCs
|
||||
dev = qdev_new("atv4.anc");
|
||||
ATV4ANCState *anc0_state = ATV4_ANC(dev);
|
||||
nms->anc0_state = anc0_state;
|
||||
memory_region_add_subregion(sysmem, PPNNPL_MEM_BASE, &anc0_state->ppnnpl_iomem);
|
||||
memory_region_add_subregion(sysmem, ANC0_MEM_BASE, &anc0_state->anc_iomem);
|
||||
|
||||
dev = qdev_new("atv4.anc");
|
||||
ATV4ANCState *anc1_state = ATV4_ANC(dev);
|
||||
nms->anc1_state = anc1_state;
|
||||
memory_region_add_subregion(sysmem, PPNNPL_MEM_BASE, &anc1_state->ppnnpl_iomem);
|
||||
memory_region_add_subregion(sysmem, ANC1_MEM_BASE, &anc1_state->anc_iomem);
|
||||
|
||||
// init spis
|
||||
uint64_t spi_bases[] = { SPI0_MEM_BASE, SPI1_MEM_BASE, SPI2_MEM_BASE, SPI3_MEM_BASE };
|
||||
for(int i = 0; i < 4; i++) {
|
||||
set_spi_base(i);
|
||||
dev = sysbus_create_simple("atv4.spi", spi_bases[i], 0); // TODO set the right IRQ!
|
||||
ATV4SPIState *spi_state = ATV4_SPI(dev);
|
||||
nms->spi_state[i] = spi_state;
|
||||
}
|
||||
|
||||
qemu_register_reset(atv4_cpu_reset, nms);
|
||||
}
|
||||
|
||||
static void atv4_instance_init(Object *obj)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void atv4_machine_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(klass);
|
||||
|
||||
mc->desc = "ATV4";
|
||||
mc->init = atv4_machine_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("max");
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_machine_info = {
|
||||
.name = TYPE_ATV4_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(ATV4MachineState),
|
||||
.class_size = sizeof(ATV4MachineClass),
|
||||
.class_init = atv4_machine_class_init,
|
||||
.instance_init = atv4_instance_init,
|
||||
};
|
||||
|
||||
static void atv4_machine_types(void)
|
||||
{
|
||||
type_register_static(&atv4_machine_info);
|
||||
}
|
||||
|
||||
type_init(atv4_machine_types)
|
||||
@@ -1,61 +0,0 @@
|
||||
#include "hw/arm/atv4_anc.h"
|
||||
|
||||
static uint64_t atv4_anc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ATV4ANCState *s = (ATV4ANCState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
default:
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
static void atv4_anc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
ATV4ANCState *s = (ATV4ANCState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps atv4_anc_ops = {
|
||||
.read = atv4_anc_read,
|
||||
.write = atv4_anc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_anc_init(Object *obj)
|
||||
{
|
||||
ATV4ANCState *s = ATV4_ANC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->ppnnpl_iomem, obj, &atv4_anc_ops, s, TYPE_ATV4_ANC, 0x100); // TODO hard-coded anc_ops call, should change
|
||||
sysbus_init_mmio(sbd, &s->ppnnpl_iomem);
|
||||
|
||||
memory_region_init_io(&s->anc_iomem, obj, &atv4_anc_ops, s, TYPE_ATV4_ANC, 0x3000);
|
||||
sysbus_init_mmio(sbd, &s->anc_iomem);
|
||||
}
|
||||
|
||||
static void atv4_anc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_anc_type_info = {
|
||||
.name = TYPE_ATV4_ANC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4ANCState),
|
||||
.instance_init = atv4_anc_init,
|
||||
.class_init = atv4_anc_class_init,
|
||||
};
|
||||
|
||||
static void atv4_anc_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_anc_type_info);
|
||||
}
|
||||
|
||||
type_init(atv4_anc_register_types)
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "hw/arm/atv4_ausb.h"
|
||||
|
||||
static uint64_t atv4_ausb_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ATV4AUSBState *s = (ATV4AUSBState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
default:
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
static void atv4_ausb_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
ATV4AUSBState *s = (ATV4AUSBState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps atv4_ausb_ops = {
|
||||
.read = atv4_ausb_read,
|
||||
.write = atv4_ausb_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_ausb_init(Object *obj)
|
||||
{
|
||||
ATV4AUSBState *s = ATV4_AUSB(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &atv4_ausb_ops, s, TYPE_ATV4_AUSB, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void atv4_ausb_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_ausb_type_info = {
|
||||
.name = TYPE_ATV4_AUSB,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4AUSBState),
|
||||
.instance_init = atv4_ausb_init,
|
||||
.class_init = atv4_ausb_class_init,
|
||||
};
|
||||
|
||||
static void atv4_ausb_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_ausb_type_info);
|
||||
}
|
||||
|
||||
type_init(atv4_ausb_register_types)
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "hw/arm/atv4_ausb_widget.h"
|
||||
|
||||
static uint64_t atv4_ausb_widget_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ATV4AUSBWidgetState *s = (ATV4AUSBWidgetState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
default:
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
static void atv4_ausb_widget_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
ATV4AUSBWidgetState *s = (ATV4AUSBWidgetState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps atv4_ausb_widget_ops = {
|
||||
.read = atv4_ausb_widget_read,
|
||||
.write = atv4_ausb_widget_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_ausb_widget_init(Object *obj)
|
||||
{
|
||||
ATV4AUSBWidgetState *s = ATV4_AUSB_WIDGET(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &atv4_ausb_widget_ops, s, TYPE_ATV4_AUSB_WIDGET, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void atv4_ausb_widget_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_ausb_widget_type_info = {
|
||||
.name = TYPE_ATV4_AUSB_WIDGET,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4AUSBWidgetState),
|
||||
.instance_init = atv4_ausb_widget_init,
|
||||
.class_init = atv4_ausb_widget_class_init,
|
||||
};
|
||||
|
||||
static void atv4_ausb_widget_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_ausb_widget_type_info);
|
||||
}
|
||||
|
||||
type_init(atv4_ausb_widget_register_types)
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "hw/arm/atv4_chipid.h"
|
||||
|
||||
static uint64_t atv4_chipid_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rCFG_FUSE0:
|
||||
return 0;
|
||||
case CHIPID_REVISION:
|
||||
return 1;
|
||||
default:
|
||||
hw_error("%s: reading from unknown chip ID register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps atv4_chipid_ops = {
|
||||
.read = atv4_chipid_read,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_chipid_init(Object *obj)
|
||||
{
|
||||
ATV4ChipIDState *s = ATV4_CHIPID(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &atv4_chipid_ops, s, TYPE_ATV4_CHIPID, 0x14);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void atv4_chipid_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_chipid_type_info = {
|
||||
.name = TYPE_ATV4_CHIPID,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4ChipIDState),
|
||||
.instance_init = atv4_chipid_init,
|
||||
.class_init = atv4_chipid_class_init,
|
||||
};
|
||||
|
||||
static void atv4_chipid_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_chipid_type_info);
|
||||
}
|
||||
|
||||
type_init(atv4_chipid_register_types)
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "hw/arm/atv4_gpio.h"
|
||||
|
||||
static void atv4_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4GPIOState *s = (struct ATV4GPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
ATV4GPIOState *s = (struct ATV4GPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gpio_ops = {
|
||||
.read = atv4_gpio_read,
|
||||
.write = atv4_gpio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_gpio_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
ATV4GPIOState *s = ATV4_GPIO(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gpio_ops, s, "gpio", 0x1000);
|
||||
}
|
||||
|
||||
static void atv4_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_gpio_info = {
|
||||
.name = TYPE_ATV4_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4GPIOState),
|
||||
.instance_init = atv4_gpio_init,
|
||||
.class_init = atv4_gpio_class_init,
|
||||
};
|
||||
|
||||
static void atv4_gpio_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_gpio_info);
|
||||
}
|
||||
|
||||
type_init(atv4_gpio_register_types)
|
||||
@@ -1,400 +0,0 @@
|
||||
#include "hw/arm/atv4_pmgr.h"
|
||||
|
||||
static uint64_t atv4_pmgr_pll_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
uint8_t pll_index = addr / 0x1000;
|
||||
if(pll_index >= 6) {
|
||||
hw_error("%s: Trying to access non-existant PLL %d\n", __func__, pll_index);
|
||||
}
|
||||
|
||||
if(addr % 0x1000 == rPMGR_PLL_EXT_BYPASS_CTL) {
|
||||
return s->pll_bypass_cfg[pll_index];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_pll_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
uint8_t pll_index = addr / 0x1000;
|
||||
if(pll_index >= 6) {
|
||||
hw_error("%s: Trying to access non-existant PLL %d\n", __func__, pll_index);
|
||||
}
|
||||
|
||||
if(addr % 0x1000 == rPMGR_PLL_EXT_BYPASS_CTL) {
|
||||
if(value & PMGR_PLL_EXT_BYPASS) {
|
||||
printf("Enabling PLL %d bypass\n", pll_index);
|
||||
s->pll_bypass_cfg[pll_index] |= PMGR_PLL_BYP_ENABLED;
|
||||
}
|
||||
else {
|
||||
printf("Disabling PLL %d bypass\n", pll_index);
|
||||
s->pll_bypass_cfg[pll_index] &= ~PMGR_PLL_BYP_ENABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_volman_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_VOLMAN_CTL:
|
||||
// TODO
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR volman register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_volman_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_ps_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_CPU0_PS ... rPMGR_VENC_ME1_PS:
|
||||
return 0xFF; // turned on
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR PS register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_ps_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_gfx_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_GFX_PERF_STATE_CTL:
|
||||
// TODO return the right GFX state
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR GFX register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_gfx_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_acg_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_MISC_ACG:
|
||||
// TODO return the right ACG state
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR ACG register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_acg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_clk_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_CLK_DIVIDER_ACG_CFG:
|
||||
// TODO return the right CLK state
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR CLK register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_clk_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_clkcfg_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_MCU_FIXED_CLK_CFG ... rPMGR_ISP_REF1_CLK_CFG:
|
||||
// TODO return the right CLK configuration
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR CLK CFG register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_clkcfg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_soc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case rPMGR_SOC_PERF_STATE_CTL:
|
||||
// TODO return the right SOC configuration
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR SOC state register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_soc_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
s->scratch[addr] = value;
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_scratch_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
printf("%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
return s->scratch[0] | (BOOT_DEVICE_NAND << 8);
|
||||
break;
|
||||
case 0x4 ... 0xC:
|
||||
return s->scratch[addr];
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR SCRATCH register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_scratch_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atv4_pmgr_pwrgate_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case 0x0 ... 0x300:
|
||||
// TODO return the right scratch configuration
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: reading from unknown PMGR PWRGATE register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atv4_pmgr_pwrgate_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
ATV4PMGRState *s = (struct ATV4PMGRState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_pll_ops = {
|
||||
.read = atv4_pmgr_pll_read,
|
||||
.write = atv4_pmgr_pll_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_volman_ops = {
|
||||
.read = atv4_pmgr_volman_read,
|
||||
.write = atv4_pmgr_volman_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_ps_ops = {
|
||||
.read = atv4_pmgr_ps_read,
|
||||
.write = atv4_pmgr_ps_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_gfx_ops = {
|
||||
.read = atv4_pmgr_gfx_read,
|
||||
.write = atv4_pmgr_gfx_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_acg_ops = {
|
||||
.read = atv4_pmgr_acg_read,
|
||||
.write = atv4_pmgr_acg_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_clk_ops = {
|
||||
.read = atv4_pmgr_clk_read,
|
||||
.write = atv4_pmgr_clk_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_clkcfg_ops = {
|
||||
.read = atv4_pmgr_clkcfg_read,
|
||||
.write = atv4_pmgr_clkcfg_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_soc_ops = {
|
||||
.read = atv4_pmgr_soc_read,
|
||||
.write = atv4_pmgr_soc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_scratch_ops = {
|
||||
.read = atv4_pmgr_scratch_read,
|
||||
.write = atv4_pmgr_scratch_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps atv4_pmgr_pwrgate_ops = {
|
||||
.read = atv4_pmgr_pwrgate_read,
|
||||
.write = atv4_pmgr_pwrgate_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_pmgr_init(Object *obj)
|
||||
{
|
||||
ATV4PMGRState *s = ATV4_PMGR(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
// PLLs
|
||||
memory_region_init_io(&s->pll_iomem, obj, &atv4_pmgr_pll_ops, s, TYPE_ATV4_PMGR_PLL, 0x10000);
|
||||
sysbus_init_mmio(sbd, &s->pll_iomem);
|
||||
|
||||
// voltage manager
|
||||
memory_region_init_io(&s->volman_iomem, obj, &atv4_pmgr_volman_ops, s, TYPE_ATV4_PMGR_VOLMAN, 0x4);
|
||||
sysbus_init_mmio(sbd, &s->volman_iomem);
|
||||
|
||||
// power states
|
||||
memory_region_init_io(&s->ps_iomem, obj, &atv4_pmgr_ps_ops, s, TYPE_ATV4_PMGR_PS, 0x1010);
|
||||
sysbus_init_mmio(sbd, &s->ps_iomem);
|
||||
|
||||
// graphics subsystem
|
||||
memory_region_init_io(&s->gfx_iomem, obj, &atv4_pmgr_gfx_ops, s, TYPE_ATV4_PMGR_GFX, 0x4);
|
||||
sysbus_init_mmio(sbd, &s->gfx_iomem);
|
||||
|
||||
// automatic clock gating
|
||||
memory_region_init_io(&s->acg_iomem, obj, &atv4_pmgr_acg_ops, s, TYPE_ATV4_PMGR_ACG, 0x4);
|
||||
sysbus_init_mmio(sbd, &s->acg_iomem);
|
||||
|
||||
// clock
|
||||
memory_region_init_io(&s->clk_iomem, obj, &atv4_pmgr_clk_ops, s, TYPE_ATV4_PMGR_CLK, 0x4);
|
||||
sysbus_init_mmio(sbd, &s->clk_iomem);
|
||||
|
||||
// clock configurations
|
||||
memory_region_init_io(&s->clkcfg_iomem, obj, &atv4_pmgr_clkcfg_ops, s, TYPE_ATV4_PMGR_CLKCFG, 0x6008);
|
||||
sysbus_init_mmio(sbd, &s->clkcfg_iomem);
|
||||
|
||||
// SOC performance
|
||||
memory_region_init_io(&s->soc_iomem, obj, &atv4_pmgr_soc_ops, s, TYPE_ATV4_PMGR_SOC, 0xFF);
|
||||
sysbus_init_mmio(sbd, &s->soc_iomem);
|
||||
|
||||
// scratch
|
||||
memory_region_init_io(&s->scratch_iomem, obj, &atv4_pmgr_scratch_ops, s, TYPE_ATV4_PMGR_SCRATCH, 0x60);
|
||||
sysbus_init_mmio(sbd, &s->scratch_iomem);
|
||||
|
||||
// power gate
|
||||
memory_region_init_io(&s->pwrgate_iomem, obj, &atv4_pmgr_pwrgate_ops, s, TYPE_ATV4_PMGR_PWRGATE, 0x300);
|
||||
sysbus_init_mmio(sbd, &s->pwrgate_iomem);
|
||||
}
|
||||
|
||||
static void atv4_pmgr_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_pmgr_type_info = {
|
||||
.name = TYPE_ATV4_PMGR,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4PMGRState),
|
||||
.instance_init = atv4_pmgr_init,
|
||||
.class_init = atv4_pmgr_class_init,
|
||||
};
|
||||
|
||||
static void atv4_pmgr_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_pmgr_type_info);
|
||||
}
|
||||
|
||||
type_init(atv4_pmgr_register_types)
|
||||
@@ -0,0 +1,535 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/arm/exynos4210.h"
|
||||
#include "hw/arm/ipod_touch_2g.h"
|
||||
#include "target/arm/cpregs.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define VMSTATE_IT2G_CPREG(name) \
|
||||
VMSTATE_UINT64(IT2G_CPREG_VAR_NAME(name), IPodTouchMachineState)
|
||||
|
||||
#define IT2G_CPREG_DEF(p_name, p_op0, p_op1, p_crn, p_crm, p_op2, p_access, p_reset) \
|
||||
{ \
|
||||
.cp = 15, \
|
||||
.name = #p_name, .opc0 = p_op0, .crn = p_crn, .crm = p_crm, \
|
||||
.opc1 = p_op1, .opc2 = p_op2, .access = p_access, .resetvalue = p_reset, \
|
||||
.state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, \
|
||||
.fieldoffset = offsetof(IPodTouchMachineState, IT2G_CPREG_VAR_NAME(p_name)) \
|
||||
- offsetof(ARMCPU, env) \
|
||||
}
|
||||
|
||||
static void allocate_ram(MemoryRegion *top, const char *name, uint32_t addr, uint32_t size)
|
||||
{
|
||||
MemoryRegion *sec = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(sec, NULL, name, size, &error_fatal);
|
||||
memory_region_add_subregion(top, addr, sec);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo it2g_cp_reginfo_tcg[] = {
|
||||
IT2G_CPREG_DEF(REG0, 0, 0, 7, 6, 0, PL1_RW, 0),
|
||||
IT2G_CPREG_DEF(REG1, 0, 0, 15, 2, 4, PL1_RW, 0),
|
||||
IT2G_CPREG_DEF(REG1, 0, 0, 7, 14, 0, PL1_RW, 0),
|
||||
IT2G_CPREG_DEF(REG1, 0, 0, 7, 10, 0, PL1_RW, 0),
|
||||
};
|
||||
|
||||
static void ipod_touch_cpu_setup(MachineState *machine, MemoryRegion **sysmem, ARMCPU **cpu, AddressSpace **nsas)
|
||||
{
|
||||
Object *cpuobj = object_new(machine->cpu_type);
|
||||
*cpu = ARM_CPU(cpuobj);
|
||||
CPUState *cs = CPU(*cpu);
|
||||
|
||||
*sysmem = get_system_memory();
|
||||
|
||||
object_property_set_link(cpuobj, "memory", OBJECT(*sysmem), &error_abort);
|
||||
|
||||
object_property_set_bool(cpuobj, "has_el3", false, NULL);
|
||||
|
||||
object_property_set_bool(cpuobj, "has_el2", false, NULL);
|
||||
|
||||
object_property_set_bool(cpuobj, "realized", true, &error_fatal);
|
||||
|
||||
*nsas = cpu_get_address_space(cs, ARMASIdx_NS);
|
||||
|
||||
define_arm_cp_regs(*cpu, it2g_cp_reginfo_tcg);
|
||||
|
||||
object_unref(cpuobj);
|
||||
}
|
||||
|
||||
static void ipod_touch_cpu_reset(void *opaque)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE((MachineState *)opaque);
|
||||
ARMCPU *cpu = nms->cpu;
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
cpu_reset(cs);
|
||||
|
||||
//env->regs[0] = nms->kbootargs_pa;
|
||||
//cpu_set_pc(CPU(cpu), 0xc00607ec);
|
||||
cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
|
||||
//env->regs[0] = 0x9000000;
|
||||
//cpu_set_pc(CPU(cpu), LLB_BASE + 0x100);
|
||||
//cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
|
||||
}
|
||||
|
||||
static void ipod_touch_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
|
||||
|
||||
allocate_ram(sysmem, "insecure_ram", INSECURE_RAM_MEM_BASE, 0x3000000);
|
||||
allocate_ram(sysmem, "secure_ram", SECURE_RAM_MEM_BASE, 0x4B04000);
|
||||
allocate_ram(sysmem, "iboot", IBOOT_MEM_BASE, 0x100000);
|
||||
allocate_ram(sysmem, "llb", 0x22000000, 0x100000);
|
||||
allocate_ram(sysmem, "sram1", SRAM1_MEM_BASE, 0x100000);
|
||||
allocate_ram(sysmem, "framebuffer", FRAMEBUFFER_MEM_BASE, 0x400000);
|
||||
allocate_ram(sysmem, "edgeic", EDGEIC_MEM_BASE, 0x1000);
|
||||
allocate_ram(sysmem, "swi", SWI_MEM_BASE, 0x1000);
|
||||
allocate_ram(sysmem, "h264", H264_MEM_BASE, 0x4000);
|
||||
|
||||
// load the bootrom (vrom)
|
||||
uint8_t *file_data = NULL;
|
||||
unsigned long fsize;
|
||||
if (g_file_get_contents(nms->bootrom_path, (char **)&file_data, &fsize, NULL)) {
|
||||
allocate_ram(sysmem, "vrom", 0x0, 0x20000);
|
||||
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_bootrom_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->bootrom_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_bootrom_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
gboolean bootrom_exists = g_file_test(value, G_FILE_TEST_EXISTS);
|
||||
if(!bootrom_exists) {
|
||||
error_report("bootrom at path \"%s\" must exist", value);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->bootrom_path, value, sizeof(nms->bootrom_path));
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_nor_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->nor_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_nor_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
gboolean nor_exists = g_file_test(value, G_FILE_TEST_EXISTS);
|
||||
if(!nor_exists) {
|
||||
error_report("NOR at path \"%s\" must exist", value);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->nor_path, value, sizeof(nms->nor_path));
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_nand_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->nand_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_nand_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
gboolean nand_exists = g_file_test(value, G_FILE_TEST_IS_DIR);
|
||||
if(!nand_exists) {
|
||||
error_report("NAND at path \"%s\" must be a directory", value);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->nand_path, value, sizeof(nms->nand_path));
|
||||
}
|
||||
|
||||
static void ipod_touch_instance_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "bootrom", ipod_touch_get_bootrom_path, ipod_touch_set_bootrom_path);
|
||||
object_property_set_description(obj, "bootrom", "Path to the S5L8720 bootrom binary");
|
||||
|
||||
object_property_add_str(obj, "nor", ipod_touch_get_nor_path, ipod_touch_set_nor_path);
|
||||
object_property_set_description(obj, "nor", "Path to the S5L8720 NOR image");
|
||||
|
||||
object_property_add_str(obj, "nand", ipod_touch_get_nand_path, ipod_touch_set_nand_path);
|
||||
object_property_set_description(obj, "nand", "Path to the NAND files");
|
||||
}
|
||||
|
||||
static inline qemu_irq s5l8900_get_irq(IPodTouchMachineState *s, int n)
|
||||
{
|
||||
return s->irq[n / S5L8720_VIC_SIZE][n % S5L8720_VIC_SIZE];
|
||||
}
|
||||
|
||||
static uint32_t s5l8720_usb_hwcfg[] = {
|
||||
0,
|
||||
0x7a8f60d0,
|
||||
0x082000e8,
|
||||
0x01f08024
|
||||
};
|
||||
|
||||
static void ipod_touch_key_event(void *opaque, int keycode)
|
||||
{
|
||||
bool do_irq = false;
|
||||
int gpio_group = 0, gpio_selector = 0;
|
||||
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
if(keycode == 25 || keycode == 153) {
|
||||
// power button
|
||||
gpio_group = GPIO_BUTTON_POWER_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_POWER_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == 25 && (s->gpio_state->gpio_state & (1 << (GPIO_BUTTON_POWER & 0xf))) == 0) {
|
||||
s->gpio_state->gpio_state |= (1 << (GPIO_BUTTON_POWER & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
else if(keycode == 153) {
|
||||
s->gpio_state->gpio_state &= ~(1 << (GPIO_BUTTON_POWER & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
}
|
||||
else if(keycode == 35 || keycode == 163) {
|
||||
// home button
|
||||
gpio_group = GPIO_BUTTON_HOME_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_HOME_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == 35 && (s->gpio_state->gpio_state & (1 << (GPIO_BUTTON_HOME & 0xf))) == 0) {
|
||||
s->gpio_state->gpio_state |= (1 << (GPIO_BUTTON_HOME & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
else if(keycode == 163) {
|
||||
s->gpio_state->gpio_state &= ~(1 << (GPIO_BUTTON_HOME & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(do_irq) {
|
||||
s->sysic->gpio_int_status[gpio_group] |= (1 << gpio_selector);
|
||||
qemu_irq_raise(s->sysic->gpio_irqs[gpio_group]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_machine_init(MachineState *machine)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
|
||||
MemoryRegion *sysmem;
|
||||
AddressSpace *nsas;
|
||||
ARMCPU *cpu;
|
||||
|
||||
ipod_touch_cpu_setup(machine, &sysmem, &cpu, &nsas);
|
||||
|
||||
// setup clock
|
||||
nms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(nms->sysclk, 12000000ULL);
|
||||
|
||||
nms->cpu = cpu;
|
||||
nms->nsas = nsas;
|
||||
|
||||
// setup VICs
|
||||
nms->irq = g_malloc0(sizeof(qemu_irq *) * 2);
|
||||
DeviceState *dev = pl192_manual_init("vic0", qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_IRQ), qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_FIQ), NULL);
|
||||
PL192State *s = PL192(dev);
|
||||
nms->vic0 = s;
|
||||
memory_region_add_subregion(sysmem, VIC0_MEM_BASE, &nms->vic0->iomem);
|
||||
nms->irq[0] = g_malloc0(sizeof(qemu_irq) * 32);
|
||||
for (int i = 0; i < 32; i++) { nms->irq[0][i] = qdev_get_gpio_in(dev, i); }
|
||||
|
||||
dev = pl192_manual_init("vic1", NULL);
|
||||
s = PL192(dev);
|
||||
nms->vic1 = s;
|
||||
memory_region_add_subregion(sysmem, VIC1_MEM_BASE, &nms->vic1->iomem);
|
||||
nms->irq[1] = g_malloc0(sizeof(qemu_irq) * 32);
|
||||
for (int i = 0; i < 32; i++) { nms->irq[1][i] = qdev_get_gpio_in(dev, i); }
|
||||
|
||||
// // chain VICs together
|
||||
nms->vic1->daisy = nms->vic0;
|
||||
|
||||
// init clock 0
|
||||
dev = qdev_new("ipodtouch.clock");
|
||||
IPodTouchClockState *clock0_state = IPOD_TOUCH_CLOCK(dev);
|
||||
nms->clock0 = clock0_state;
|
||||
memory_region_add_subregion(sysmem, CLOCK0_MEM_BASE, &clock0_state->iomem);
|
||||
|
||||
// init clock 1
|
||||
dev = qdev_new("ipodtouch.clock");
|
||||
IPodTouchClockState *clock1_state = IPOD_TOUCH_CLOCK(dev);
|
||||
nms->clock1 = clock1_state;
|
||||
memory_region_add_subregion(sysmem, CLOCK1_MEM_BASE, &clock1_state->iomem);
|
||||
|
||||
// init the timer
|
||||
dev = qdev_new("ipodtouch.timer");
|
||||
IPodTouchTimerState *timer_state = IPOD_TOUCH_TIMER(dev);
|
||||
nms->timer1 = timer_state;
|
||||
memory_region_add_subregion(sysmem, TIMER1_MEM_BASE, &timer_state->iomem);
|
||||
SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ));
|
||||
//sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ - 1));
|
||||
timer_state->sysclk = nms->sysclk;
|
||||
|
||||
// init sysic
|
||||
dev = qdev_new("ipodtouch.sysic");
|
||||
IPodTouchSYSICState *sysic_state = IPOD_TOUCH_SYSIC(dev);
|
||||
nms->sysic = (IPodTouchSYSICState *) g_malloc0(sizeof(struct IPodTouchSYSICState));
|
||||
memory_region_add_subregion(sysmem, SYSIC_MEM_BASE, &sysic_state->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
|
||||
sysbus_connect_irq(busdev, grp, s5l8900_get_irq(nms, S5L8900_GPIO_IRQS[grp]));
|
||||
}
|
||||
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G0_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G1_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G2_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G3_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G4_IRQ));
|
||||
|
||||
// init GPIO
|
||||
dev = qdev_new("ipodtouch.gpio");
|
||||
IPodTouchGPIOState *gpio_state = IPOD_TOUCH_GPIO(dev);
|
||||
nms->gpio_state = gpio_state;
|
||||
memory_region_add_subregion(sysmem, GPIO_MEM_BASE, &gpio_state->iomem);
|
||||
|
||||
// init SDIO
|
||||
dev = qdev_new("ipodtouch.sdio");
|
||||
IPodTouchSDIOState *sdio_state = IPOD_TOUCH_SDIO(dev);
|
||||
nms->sdio_state = sdio_state;
|
||||
memory_region_add_subregion(sysmem, SDIO_MEM_BASE, &sdio_state->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_SDIO_IRQ));
|
||||
|
||||
dev = exynos4210_uart_create(UART0_MEM_BASE, 256, 0, serial_hd(0), nms->irq[0][24]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
dev = exynos4210_uart_create(UART1_MEM_BASE, 256, 1, serial_hd(1), nms->irq[0][25]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
dev = exynos4210_uart_create(UART2_MEM_BASE, 256, 2, serial_hd(2), nms->irq[0][26]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
dev = exynos4210_uart_create(UART3_MEM_BASE, 256, 3, serial_hd(3), nms->irq[0][27]);
|
||||
if (!dev) {
|
||||
hw_error("Failed to create UART0 device!");
|
||||
}
|
||||
|
||||
// dev = exynos4210_uart_create(UART4_MEM_BASE, 256, 4, serial_hd(4), nms->irq[0][28]);
|
||||
// if (!dev) {
|
||||
// printf("Failed to create uart4 device!\n");
|
||||
// abort();
|
||||
// }
|
||||
|
||||
// init spis
|
||||
set_spi_base(0);
|
||||
dev = sysbus_create_simple("ipodtouch.spi", SPI0_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI0_IRQ));
|
||||
IPodTouchSPIState *spi0_state = IPOD_TOUCH_SPI(dev);
|
||||
spi0_state->nor->nor_path = nms->nor_path;
|
||||
nms->spi0_state = spi0_state;
|
||||
|
||||
set_spi_base(1);
|
||||
dev = sysbus_create_simple("ipodtouch.spi", SPI1_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI1_IRQ));
|
||||
IPodTouchSPIState *spi1_state = IPOD_TOUCH_SPI(dev);
|
||||
nms->spi1_state = spi1_state;
|
||||
|
||||
set_spi_base(2);
|
||||
sysbus_create_simple("ipodtouch.spi", SPI2_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI2_IRQ));
|
||||
|
||||
set_spi_base(3);
|
||||
sysbus_create_simple("ipodtouch.spi", SPI3_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI3_IRQ));
|
||||
|
||||
set_spi_base(4);
|
||||
dev = sysbus_create_simple("ipodtouch.spi", SPI4_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI4_IRQ));
|
||||
IPodTouchSPIState *spi4_state = IPOD_TOUCH_SPI(dev);
|
||||
spi4_state->mt->sysic = sysic_state;
|
||||
spi4_state->mt->gpio_state = gpio_state;
|
||||
nms->spi4_state = spi4_state;
|
||||
|
||||
// init the chip ID module
|
||||
dev = qdev_new("ipodtouch.chipid");
|
||||
IPodTouchChipIDState *chipid_state = IPOD_TOUCH_CHIPID(dev);
|
||||
nms->chipid_state = chipid_state;
|
||||
memory_region_add_subregion(sysmem, CHIPID_MEM_BASE, &chipid_state->iomem);
|
||||
|
||||
// init the TVOut instance
|
||||
dev = qdev_new("ipodtouch.tvout");
|
||||
IPodTouchTVOutState *tvout_state = IPOD_TOUCH_TVOUT(dev);
|
||||
nms->tvout_state = tvout_state;
|
||||
memory_region_add_subregion(sysmem, TVOUT_MIXER1_MEM_BASE, &tvout_state->mixer1_iomem);
|
||||
memory_region_add_subregion(sysmem, TVOUT_MIXER2_MEM_BASE, &tvout_state->mixer2_iomem);
|
||||
memory_region_add_subregion(sysmem, TVOUT_SDO_MEM_BASE, &tvout_state->sdo_iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TVOUT_SDO_IRQ));
|
||||
|
||||
// init the unknown1 module
|
||||
dev = qdev_new("ipodtouch.unknown1");
|
||||
IPodTouchUnknown1State *unknown1_state = IPOD_TOUCH_UNKNOWN1(dev);
|
||||
memory_region_add_subregion(sysmem, UNKNOWN1_MEM_BASE, &unknown1_state->iomem);
|
||||
|
||||
// init USB OTG
|
||||
dev = ipod_touch_init_usb_otg(s5l8900_get_irq(nms, S5L8720_USB_OTG_IRQ), s5l8720_usb_hwcfg);
|
||||
synopsys_usb_state *usb_otg = S5L8900USBOTG(dev);
|
||||
nms->usb_otg = usb_otg;
|
||||
memory_region_add_subregion(sysmem, USBOTG_MEM_BASE, &nms->usb_otg->iomem);
|
||||
|
||||
// init two pl080 DMAC0 devices
|
||||
dev = qdev_new("pl080");
|
||||
PL080State *pl080_1 = PL080(dev);
|
||||
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
|
||||
memory_region_add_subregion(sysmem, DMAC0_MEM_BASE, &pl080_1->iomem1);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC0_IRQ));
|
||||
|
||||
dev = qdev_new("pl080");
|
||||
PL080State *pl080_2 = PL080(dev);
|
||||
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
|
||||
memory_region_add_subregion(sysmem, DMAC1_0_MEM_BASE, &pl080_2->iomem1);
|
||||
memory_region_add_subregion(sysmem, DMAC1_1_MEM_BASE, &pl080_2->iomem2);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC1_IRQ));
|
||||
|
||||
// Init I2C0
|
||||
dev = qdev_new("ipodtouch.i2c");
|
||||
IPodTouchI2CState *i2c_state = IPOD_TOUCH_I2C(dev);
|
||||
i2c_state->base = 0;
|
||||
nms->i2c0_state = i2c_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, I2C0_MEM_BASE, &i2c_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C0_IRQ));
|
||||
|
||||
// init the PMU
|
||||
I2CSlave * pmu = i2c_slave_create_simple(i2c_state->bus, "pcf50633", 0x73);
|
||||
|
||||
// init the accelerometer
|
||||
I2CSlave *accelerometer = i2c_slave_create_simple(i2c_state->bus, "lis302dl", 0x1D);
|
||||
|
||||
// Init I2C1
|
||||
dev = qdev_new("ipodtouch.i2c");
|
||||
i2c_state = IPOD_TOUCH_I2C(dev);
|
||||
nms->i2c1_state = i2c_state;
|
||||
i2c_state->base = 1;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, I2C1_MEM_BASE, &i2c_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C1_IRQ));
|
||||
|
||||
// init the Mikey
|
||||
I2CSlave *cd327mikey = i2c_slave_create_simple(i2c_state->bus, "cd3272mikey", 0x39);
|
||||
|
||||
// init the FMSS flash controller
|
||||
dev = qdev_new("ipodtouch.fmss");
|
||||
IPodTouchFMSSState *fmss_state = IPOD_TOUCH_FMSS(dev);
|
||||
fmss_state->nand_path = nms->nand_path;
|
||||
nms->fmss_state = fmss_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, FMSS_MEM_BASE, &fmss_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_FMSS_IRQ));
|
||||
|
||||
// init the USB module
|
||||
dev = qdev_new("ipodtouch.usbphys");
|
||||
IPodTouchUSBPhysState *usb_phys_state = IPOD_TOUCH_USB_PHYS(dev);
|
||||
nms->usb_phys_state = usb_phys_state;
|
||||
memory_region_add_subregion(sysmem, USBPHYS_MEM_BASE, &usb_phys_state->iomem);
|
||||
|
||||
ipod_touch_memory_setup(machine, sysmem, nsas);
|
||||
|
||||
// init the MIPI SDI controller
|
||||
dev = qdev_new("ipodtouch.mipidsi");
|
||||
IPodTouchMIPIDSIState *mipi_dsi_state = IPOD_TOUCH_MIPI_DSI(dev);
|
||||
nms->mipi_dsi_state = mipi_dsi_state;
|
||||
memory_region_add_subregion(sysmem, MIPI_DSI_MEM_BASE, &mipi_dsi_state->iomem);
|
||||
|
||||
// init LCD
|
||||
dev = qdev_new("ipodtouch.lcd");
|
||||
IPodTouchLCDState *lcd_state = IPOD_TOUCH_LCD(dev);
|
||||
lcd_state->sysmem = sysmem;
|
||||
lcd_state->mt = spi4_state->mt;
|
||||
nms->lcd_state = lcd_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, DISPLAY_MEM_BASE, &lcd_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_LCD_IRQ));
|
||||
|
||||
// init scaler / CSC
|
||||
dev = qdev_new("ipodtouch.scalercsc");
|
||||
IPodTouchScalerCSCState *scaler_csc_state = IPOD_TOUCH_SCALER_CSC(dev);
|
||||
nms->scaler_csc_state = scaler_csc_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
memory_region_add_subregion(sysmem, SCALER_CSC_MEM_BASE, &scaler_csc_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
// init SHA1 engine
|
||||
dev = qdev_new("ipodtouch.sha1");
|
||||
IPodTouchSHA1State *sha1_state = IPOD_TOUCH_SHA1(dev);
|
||||
nms->sha1_state = sha1_state;
|
||||
memory_region_add_subregion(sysmem, SHA1_MEM_BASE, &sha1_state->iomem);
|
||||
|
||||
// init AES engine
|
||||
dev = qdev_new("ipodtouch.aes");
|
||||
IPodTouchAESState *aes_state = IPOD_TOUCH_AES(dev);
|
||||
nms->aes_state = aes_state;
|
||||
memory_region_add_subregion(sysmem, AES_MEM_BASE, &aes_state->iomem);
|
||||
|
||||
// init PKE engine
|
||||
dev = qdev_new("ipodtouch.pke");
|
||||
IPodTouchPKEState *pke_state = IPOD_TOUCH_PKE(dev);
|
||||
nms->pke_state = pke_state;
|
||||
memory_region_add_subregion(sysmem, PKE_MEM_BASE, &pke_state->iomem);
|
||||
|
||||
// init the MBX
|
||||
dev = qdev_new("ipodtouch.mbx");
|
||||
IPodTouchMBXState *mbx_state = IPOD_TOUCH_MBX(dev);
|
||||
nms->mbx_state = mbx_state;
|
||||
memory_region_add_subregion(sysmem, MBX1_MEM_BASE, &mbx_state->iomem1);
|
||||
memory_region_add_subregion(sysmem, MBX2_MEM_BASE, &mbx_state->iomem2);
|
||||
|
||||
qemu_register_reset(ipod_touch_cpu_reset, nms);
|
||||
|
||||
qemu_add_kbd_event_handler(ipod_touch_key_event, spi4_state->mt);
|
||||
}
|
||||
|
||||
static void ipod_touch_machine_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(klass);
|
||||
mc->desc = "iPod Touch";
|
||||
mc->init = ipod_touch_machine_init;
|
||||
mc->max_cpus = 1;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1176");
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_machine_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(IPodTouchMachineState),
|
||||
.class_size = sizeof(IPodTouchMachineClass),
|
||||
.class_init = ipod_touch_machine_class_init,
|
||||
.instance_init = ipod_touch_instance_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_machine_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,203 @@
|
||||
#include "hw/arm/ipod_touch_aes.h"
|
||||
|
||||
static uint64_t ipod_touch_aes_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
struct IPodTouchAESState *aesop = (struct IPodTouchAESState *)opaque;
|
||||
|
||||
switch(offset) {
|
||||
case AES_STATUS:
|
||||
return aesop->status;
|
||||
default:
|
||||
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x\n", __FUNCTION__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_aes_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
struct IPodTouchAESState *aesop = (struct IPodTouchAESState *)opaque;
|
||||
|
||||
uint8_t *inbuf;
|
||||
uint8_t *buf;
|
||||
|
||||
// fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
uint32_t *data = malloc(sizeof(uint16_t) * 9);
|
||||
|
||||
switch(offset) {
|
||||
case AES_GO:
|
||||
// PATCH OUT
|
||||
// in iBoot, we also patch the length check after LZSS compression since that can lead to issues
|
||||
|
||||
inbuf = (uint8_t *)malloc(aesop->insize);
|
||||
cpu_physical_memory_read((aesop->inaddr), inbuf, aesop->insize);
|
||||
|
||||
switch(aesop->keytype) {
|
||||
case AESGID:
|
||||
break;
|
||||
case AESUID:
|
||||
AES_set_decrypt_key(key_uid, sizeof(key_uid) * 8, &aesop->decryptKey);
|
||||
break;
|
||||
case AESCustom:
|
||||
AES_set_decrypt_key((uint8_t *)(&aesop->custkey[4]), 0x10 * 8, &aesop->decryptKey);
|
||||
break;
|
||||
}
|
||||
|
||||
buf = (uint8_t *) malloc(aesop->insize);
|
||||
printf("In size: %d, out size: %d, in addr: 0x%08x, in buf: 0x%08x, out addr: 0x%08x, %d\n", aesop->insize, aesop->outsize, aesop->inaddr, ((uint32_t *)inbuf)[0], aesop->outaddr, aesop->gid_encryption_count);
|
||||
|
||||
if(aesop->keytype == AESGID) {
|
||||
// Unfortunately, we don't have access to the GID key.
|
||||
// However, we know that when the AES engine is invoked with the GID key type, it's for the decryption of the IMG file.
|
||||
// Instead, we provide an hard-coded key as result and copy it to the output buffer.
|
||||
// source: https://www.theiphonewiki.com/wiki/Sugar_Bowl_5F138_(iPod2,1)
|
||||
if(aesop->gid_encryption_count == 0) { // LLB
|
||||
char key[] = {
|
||||
0xce, 0x97, 0xa7, 0xc8, 0x2e, 0xf8, 0x64, 0x67, 0x5e, 0xd3, 0x68, 0x05, 0x97, 0xec, 0x2a, 0xef, // IV
|
||||
0x27, 0x73, 0x2a, 0x6b, 0xbf, 0xb1, 0x4a, 0x07, 0x25, 0x0a, 0x2e, 0x46, 0x82, 0xbf, 0x3c, 0xba, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 1) { // iBoot
|
||||
char key[] = {
|
||||
0xb3, 0x63, 0x3a, 0xfb, 0xe0, 0x2e, 0x0e, 0x9b, 0xa4, 0xd7, 0x36, 0x6c, 0x47, 0xab, 0xe5, 0xa8, // IV
|
||||
0x2d, 0x91, 0x6d, 0xab, 0xb6, 0xdf, 0xd4, 0x59, 0x4d, 0xbe, 0x36, 0x35, 0xb4, 0xc7, 0x16, 0x62, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 2) { // apple logo
|
||||
|
||||
// very ugly - we patch out here the LZSS check
|
||||
data[0] = 0x0; // NOP
|
||||
cpu_physical_memory_write(0x0ff119f0, (uint8_t *)data, 4);
|
||||
|
||||
char key[] = {
|
||||
0x64, 0x23, 0x8f, 0xb0, 0x32, 0x91, 0x42, 0x25, 0x22, 0xb5, 0xdd, 0x28, 0x3f, 0xc3, 0x89, 0x5c, // IV
|
||||
0x85, 0x9f, 0xd4, 0xd3, 0x82, 0xb8, 0x38, 0x51, 0x56, 0xfc, 0x58, 0x1a, 0x7f, 0x1d, 0x97, 0x22, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 3) { // kernelcache
|
||||
char key[] = {
|
||||
0xa1, 0x91, 0x29, 0x12, 0x90, 0xd4, 0x87, 0xff, 0x07, 0x31, 0x96, 0x9c, 0x5f, 0xc8, 0xd9, 0x18, // IV
|
||||
0x0e, 0x4d, 0x23, 0xfa, 0x67, 0x59, 0x99, 0xd5, 0x95, 0x9d, 0xd1, 0x0c, 0x8d, 0xd7, 0x3d, 0x20, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
else if(aesop->gid_encryption_count == 4) { // device tree
|
||||
char key[] = {
|
||||
0xcc, 0xff, 0x63, 0x4e, 0xe1, 0x27, 0x35, 0xf0, 0x19, 0x16, 0xc4, 0xa6, 0xb2, 0x0f, 0xf1, 0x45, // IV
|
||||
0xe1, 0x7b, 0xcd, 0x56, 0x8d, 0xf1, 0xcd, 0xdc, 0x8f, 0xec, 0xbf, 0x54, 0x87, 0xd5, 0xc3, 0xce, // key
|
||||
};
|
||||
for(int i = 0; i < aesop->insize; i++) { buf[i] = key[i]; }
|
||||
}
|
||||
|
||||
aesop->gid_encryption_count++;
|
||||
}
|
||||
else {
|
||||
AES_cbc_encrypt(inbuf, buf, aesop->insize, &aesop->decryptKey, (uint8_t *)aesop->ivec, AES_DECRYPT);
|
||||
}
|
||||
|
||||
if(aesop->outaddr != 0x220100ac && aesop->outaddr != 0x0bf08468 && aesop->outaddr != 0x0fb9bcdc) { // TODO very ugly hack - for the RSA key decryption, it seems that doing nothing results in the correct decryption key??
|
||||
// BUG: after decrypting the kernel, we update the Adler CRC code and number of expected bytes.
|
||||
if(aesop->outaddr == 0x0b000020) {
|
||||
uint32_t *cast_buf = (uint32_t *)buf;
|
||||
cast_buf[2] = 0xA7886041; // adler
|
||||
cast_buf[3] = 0xF5D37E00; // 8311797 in big endian
|
||||
}
|
||||
|
||||
// BUG: after decrypting the device tree, the last few bytes are incorrect
|
||||
if(aesop->outaddr == 0x0bf00020) {
|
||||
uint32_t *cast_buf = (uint32_t *)buf;
|
||||
cast_buf[8429] = 0x4; // setting the size of the AAPL,phandle property to 4
|
||||
cast_buf[8430] = 0x0011C4F0; // set the right handle
|
||||
}
|
||||
|
||||
cpu_physical_memory_write((aesop->outaddr), buf, aesop->insize);
|
||||
}
|
||||
|
||||
memset(aesop->custkey, 0, 0x20);
|
||||
memset(aesop->ivec, 0, 0x10);
|
||||
free(inbuf);
|
||||
free(buf);
|
||||
aesop->outsize = aesop->insize;
|
||||
aesop->status = 0xf;
|
||||
break;
|
||||
case AES_KEYLEN:
|
||||
aesop->operation = value;
|
||||
aesop->keylen = value;
|
||||
break;
|
||||
case AES_INADDR:
|
||||
aesop->inaddr = value;
|
||||
break;
|
||||
case AES_INSIZE:
|
||||
aesop->insize = value;
|
||||
break;
|
||||
case AES_OUTSIZE:
|
||||
aesop->outsize = value;
|
||||
break;
|
||||
case AES_OUTADDR:
|
||||
aesop->outaddr = value;
|
||||
break;
|
||||
case AES_TYPE:
|
||||
aesop->keytype = value;
|
||||
break;
|
||||
case AES_KEY_REG ... ((AES_KEY_REG + AES_KEYSIZE) - 1):
|
||||
{
|
||||
uint8_t idx = (offset - AES_KEY_REG) / 4;
|
||||
aesop->custkey[idx] |= value;
|
||||
break;
|
||||
}
|
||||
case AES_IV_REG ... ((AES_IV_REG + AES_IVSIZE) -1 ):
|
||||
{
|
||||
uint8_t idx = (offset - AES_IV_REG) / 4;
|
||||
aesop->ivec[idx] |= value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x - 0x%08x\n", __FUNCTION__, offset, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aes_ops = {
|
||||
.read = ipod_touch_aes_read,
|
||||
.write = ipod_touch_aes_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_aes_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchAESState *s = IPOD_TOUCH_AES(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &aes_ops, s, "aes", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
memset(&s->custkey, 0, 8 * sizeof(uint32_t));
|
||||
memset(&s->ivec, 0, 4 * sizeof(uint32_t));
|
||||
|
||||
s->gid_encryption_count = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_aes_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_aes_info = {
|
||||
.name = TYPE_IPOD_TOUCH_AES,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchAESState),
|
||||
.instance_init = ipod_touch_aes_init,
|
||||
.class_init = ipod_touch_aes_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_aes_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "hw/arm/ipod_touch_cd3272_mikey.h"
|
||||
|
||||
static int cd3272_mikey_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t cd3272_mikey_recv(I2CSlave *i2c)
|
||||
{
|
||||
CD3272MikeyState *s = CD3272MIKEY(i2c);
|
||||
printf("Reading mikey register %d\n", s->cmd);
|
||||
|
||||
int res = 0;
|
||||
|
||||
switch(s->cmd) {
|
||||
default:
|
||||
res = 0;
|
||||
}
|
||||
|
||||
s->cmd += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int cd3272_mikey_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
CD3272MikeyState *s = CD3272MIKEY(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cd3272_mikey_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void cd3272_mikey_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = cd3272_mikey_event;
|
||||
k->recv = cd3272_mikey_recv;
|
||||
k->send = cd3272_mikey_send;
|
||||
}
|
||||
|
||||
static const TypeInfo cd3272_mikey_info = {
|
||||
.name = TYPE_CD3272MIKEY,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = cd3272_mikey_init,
|
||||
.instance_size = sizeof(CD3272MikeyState),
|
||||
.class_init = cd3272_mikey_class_init,
|
||||
};
|
||||
|
||||
static void cd3272_mikey_register_types(void)
|
||||
{
|
||||
type_register_static(&cd3272_mikey_info);
|
||||
}
|
||||
|
||||
type_init(cd3272_mikey_register_types)
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "hw/arm/ipod_touch_chipid.h"
|
||||
|
||||
static uint64_t ipod_touch_chipid_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case CHIPID_UNKNOWN1:
|
||||
return (1 << 5); // ind5 = production mode
|
||||
case CHIPID_INFO:
|
||||
return (0x8720 << 16) | (1 << 2); // ind16 = chipid, ind2 = security domain,
|
||||
case CHIPID_UNKNOWN2:
|
||||
return 0;
|
||||
case CHIPID_UNKNOWN3:
|
||||
return 0;
|
||||
default:
|
||||
hw_error("%s: reading from unknown chip ID register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_chipid_ops = {
|
||||
.read = ipod_touch_chipid_read,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_chipid_init(Object *obj)
|
||||
{
|
||||
IPodTouchChipIDState *s = IPOD_TOUCH_CHIPID(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_chipid_ops, s, TYPE_IPOD_TOUCH_CHIPID, 0x14);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_chipid_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_chipid_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_CHIPID,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchChipIDState),
|
||||
.instance_init = ipod_touch_chipid_init,
|
||||
.class_init = ipod_touch_chipid_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_chipid_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_chipid_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_chipid_register_types)
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "hw/arm/ipod_touch_clock.h"
|
||||
|
||||
static void s5l8900_clock_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case CLOCK_CONFIG0:
|
||||
s->config0 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG1:
|
||||
s->config1 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG2:
|
||||
s->config2 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG3:
|
||||
s->config3 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG4:
|
||||
s->config4 = val;
|
||||
break;
|
||||
case CLOCK_CONFIG5:
|
||||
s->config5 = val;
|
||||
break;
|
||||
|
||||
case CLOCK_PLL0CON:
|
||||
s->pll0con = val;
|
||||
break;
|
||||
case CLOCK_PLL1CON:
|
||||
s->pll1con = val;
|
||||
break;
|
||||
case CLOCK_PLL2CON:
|
||||
s->pll2con = val;
|
||||
break;
|
||||
case CLOCK_PLL3CON:
|
||||
s->pll3con = val;
|
||||
break;
|
||||
case CLOCK_PLL0LCNT:
|
||||
s->pll0lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLL1LCNT:
|
||||
s->pll1lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLL2LCNT:
|
||||
s->pll2lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLL3LCNT:
|
||||
s->pll3lcnt = val;
|
||||
break;
|
||||
case CLOCK_PLLLOCK:
|
||||
hw_error("%s: Forbidden write to PLLLOCK register 0x%08x\n", __func__, addr);
|
||||
break;
|
||||
case CLOCK_PLLMODE:
|
||||
s->pllmode = val;
|
||||
break;
|
||||
case CLOCK_PWRCON0:
|
||||
s->pwrcon0 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON1:
|
||||
s->pwrcon1 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON2:
|
||||
s->pwrcon2 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON3:
|
||||
s->pwrcon3 = val;
|
||||
break;
|
||||
case CLOCK_PWRCON4:
|
||||
s->pwrcon4 = val;
|
||||
break;
|
||||
default:
|
||||
hw_error("%s: writing value 0x%08x to unknown clock register 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_clock_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case CLOCK_CONFIG0:
|
||||
return s->config0;
|
||||
case CLOCK_CONFIG1:
|
||||
return s->config1;
|
||||
case CLOCK_CONFIG2:
|
||||
return s->config2;
|
||||
case CLOCK_CONFIG3:
|
||||
return s->config3;
|
||||
case CLOCK_CONFIG4:
|
||||
return s->config4;
|
||||
case CLOCK_CONFIG5:
|
||||
return s->config5;
|
||||
|
||||
case CLOCK_PLL0CON:
|
||||
return s->pll0con;
|
||||
case CLOCK_PLL1CON:
|
||||
return s->pll1con;
|
||||
case CLOCK_PLL2CON:
|
||||
return s->pll2con;
|
||||
case CLOCK_PLL3CON:
|
||||
return s->pll3con;
|
||||
case CLOCK_PLLLOCK:
|
||||
return (1 | 2 | 4 | 8); // all PLLs are locked
|
||||
case CLOCK_PLLMODE:
|
||||
return s->pllmode;
|
||||
case CLOCK_PWRCON0:
|
||||
return s->pwrcon0;
|
||||
case CLOCK_PWRCON1:
|
||||
return s->pwrcon1;
|
||||
case CLOCK_PWRCON2:
|
||||
return s->pwrcon2;
|
||||
case CLOCK_PWRCON3:
|
||||
return s->pwrcon3;
|
||||
case CLOCK_PWRCON4:
|
||||
return s->pwrcon4;
|
||||
default:
|
||||
hw_error("%s: reading from unknown clock register 0x%08x\n", __func__, addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps clock_ops = {
|
||||
.read = s5l8900_clock_read,
|
||||
.write = s5l8900_clock_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_clock_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchClockState *s = IPOD_TOUCH_CLOCK(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &clock_ops, s, "clock", 0x80);
|
||||
}
|
||||
|
||||
static void s5l8900_clock_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_clock_info = {
|
||||
.name = TYPE_IPOD_TOUCH_CLOCK,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchClockState),
|
||||
.instance_init = s5l8900_clock_init,
|
||||
.class_init = s5l8900_clock_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_clock_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,199 @@
|
||||
#include "hw/arm/ipod_touch_fmss.h"
|
||||
|
||||
static uint8_t find_bit_index(uint8_t num) {
|
||||
int index = 0;
|
||||
while (num > 1) {
|
||||
num >>= 1;
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static void write_chip_info(IPodTouchFMSSState *s)
|
||||
{
|
||||
uint32_t chipid[] = { 0xb614d5ad, 0xb614d5ad, 0xb614d5ad, 0xb614d5ad };
|
||||
cpu_physical_memory_write(s->reg_cinfo_target_addr, &chipid, 0x10);
|
||||
}
|
||||
|
||||
static void dump_registers(IPodTouchFMSSState *s) {
|
||||
printf("FMSS_PAGES_IN_ADDR: 0x%08x\n", s->reg_pages_in_addr);
|
||||
printf("FMSS_CS_BUF_ADDR: 0x%08x\n", s->reg_cs_buf_addr);
|
||||
printf("FMSS_NUM_PAGES: 0x%08x\n", s->reg_num_pages);
|
||||
}
|
||||
|
||||
static void read_nand_pages(IPodTouchFMSSState *s)
|
||||
{
|
||||
// boot args
|
||||
const char *boot_args = "kextlog=0xfff debug=0x8 cpus=1 rd=disk0s1 serial=1 pmu-debug=0x1 io=0xffff8fff debug-usb=0xffffffff"; // if not const then overwritten
|
||||
cpu_physical_memory_write(0x0ff2a584, boot_args, strlen(boot_args));
|
||||
|
||||
// patch iBoot - we want to inject the bluetooth MAC address which is located as sub-node of uart1 and not uart3 in the device tree...
|
||||
const char *chr = "arm-io/uart1/bluetooth";
|
||||
cpu_physical_memory_write(0x0ff2206c, chr, strlen(chr));
|
||||
|
||||
int page_out_buf_ind = 0;
|
||||
//dump_registers(s);
|
||||
//printf("Start CMD...\n");
|
||||
for(int page_ind = 0; page_ind < s->reg_num_pages; page_ind++) {
|
||||
uint32_t page_nr = 0;
|
||||
uint32_t page_out_addr = 0;
|
||||
uint32_t cs = 0;
|
||||
cpu_physical_memory_read(s->reg_pages_in_addr + (page_ind * sizeof(uint32_t)), &page_nr, sizeof(uint32_t));
|
||||
cpu_physical_memory_read(s->reg_cs_buf_addr + (page_ind * sizeof(uint32_t)), &cs, sizeof(uint32_t));
|
||||
uint32_t og_cs = cs;
|
||||
cs = find_bit_index(cs);
|
||||
|
||||
if(cs > 3) {
|
||||
printf("CS %d invalid! (original CS: %d, reading page %d)\n", cs, og_cs, page_nr);
|
||||
dump_registers(s);
|
||||
hw_error("CS %d invalid!", cs);
|
||||
}
|
||||
|
||||
// prepare the page
|
||||
char filename[200];
|
||||
sprintf(filename, "%s/cs%d/%d.page", s->nand_path, cs, page_nr);
|
||||
struct stat st = {0};
|
||||
if (stat(filename, &st) == -1) {
|
||||
// page storage does not exist - initialize an empty buffer
|
||||
memset(s->page_buffer, 0, NAND_BYTES_PER_PAGE);
|
||||
memset(s->page_spare_buffer, 0, NAND_BYTES_PER_SPARE);
|
||||
|
||||
uint32_t *buf_cst = (uint32_t *) s->page_spare_buffer;
|
||||
buf_cst[2] = 0x00FF00FF;
|
||||
}
|
||||
else {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (f == NULL) { hw_error("Unable to read file!"); }
|
||||
fread(s->page_buffer, sizeof(char), NAND_BYTES_PER_PAGE, f);
|
||||
fread(s->page_spare_buffer, sizeof(char), NAND_BYTES_PER_SPARE, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// we write away the page in two parts, 2048 bytes first and then the other 2048 bytes.
|
||||
int write_buf_size = NAND_BYTES_PER_PAGE / 2;
|
||||
for(int i = 0; i < 2; i++) {
|
||||
cpu_physical_memory_read(s->reg_pages_out_addr + (page_out_buf_ind * sizeof(uint32_t)), &page_out_addr, sizeof(uint32_t));
|
||||
cpu_physical_memory_write(page_out_addr, s->page_buffer + i * write_buf_size, write_buf_size);
|
||||
//printf("Will read page %d @ cs %d into address 0x%08x and spare into address 0x%08x\n", page_nr, cs, page_out_addr, s->reg_page_spare_out_addr);
|
||||
page_out_buf_ind++;
|
||||
|
||||
}
|
||||
|
||||
// finally, write the spare
|
||||
cpu_physical_memory_write(s->reg_page_spare_out_addr + page_ind * 0xc, s->page_spare_buffer, NAND_BYTES_PER_SPARE);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_fmss_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchFMSSState *s = (IPodTouchFMSSState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case FMSS__CS_BUF_RST_OK:
|
||||
return 0x1;
|
||||
case FMSS__CS_IRQ:
|
||||
return s->reg_cs_irq_bit;
|
||||
case FMSS__CS_IRQMASK:
|
||||
return 0x1;
|
||||
case FMSS__FMCTRL1:
|
||||
return (0x1 << 30);
|
||||
case 0xD00:
|
||||
return 42;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_fmss_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchFMSSState *s = (IPodTouchFMSSState *)opaque;
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr) {
|
||||
case 0xC00:
|
||||
if(val == 0x0000ffb5) { s->reg_cs_irq_bit = 1; } // TODO ugly and hard-coded
|
||||
if(val == 0xfff5) { s->reg_cs_irq_bit = 1; qemu_set_irq(s->irq, 1); }
|
||||
break;
|
||||
case FMSS__CS_IRQ:
|
||||
s->reg_cs_irq_bit = 0;
|
||||
qemu_set_irq(s->irq, 0);
|
||||
break;
|
||||
case FMSS_CINFO_TARGET_ADDR:
|
||||
s->reg_cinfo_target_addr = val;
|
||||
write_chip_info(s);
|
||||
break;
|
||||
case FMSS_PAGES_IN_ADDR:
|
||||
s->reg_pages_in_addr = val;
|
||||
break;
|
||||
case FMSS_CS_BUF_ADDR:
|
||||
s->reg_cs_buf_addr = val;
|
||||
break;
|
||||
case FMSS_NUM_PAGES:
|
||||
s->reg_num_pages = val;
|
||||
break;
|
||||
case FMSS_PAGE_SPARE_OUT_ADDR:
|
||||
s->reg_page_spare_out_addr = val;
|
||||
break;
|
||||
case FMSS_PAGES_OUT_ADDR:
|
||||
s->reg_pages_out_addr = val;
|
||||
break;
|
||||
case FMSS_CSGENRC:
|
||||
s->reg_csgenrc = val;
|
||||
break;
|
||||
case 0xD38:
|
||||
if(s->reg_csgenrc == 0xa01) { read_nand_pages(s); }
|
||||
else { printf("NAND write not suported yet!\n"); }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps fmss_ops = {
|
||||
.read = ipod_touch_fmss_read,
|
||||
.write = ipod_touch_fmss_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_fmss_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_fmss_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchFMSSState *s = IPOD_TOUCH_FMSS(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &fmss_ops, s, "fmss", 0xF00);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->page_buffer = (uint8_t *)malloc(NAND_BYTES_PER_PAGE);
|
||||
s->page_spare_buffer = (uint8_t *)malloc(NAND_BYTES_PER_SPARE);
|
||||
}
|
||||
|
||||
static void ipod_touch_fmss_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = ipod_touch_fmss_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_fmss_info = {
|
||||
.name = TYPE_IPOD_TOUCH_FMSS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchFMSSState),
|
||||
.instance_init = ipod_touch_fmss_init,
|
||||
.class_init = ipod_touch_fmss_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_fmss_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
|
||||
static void s5l8900_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case 0x184: // TODO: This offset only makes sure that the GPIO triggers associated with the home/power button work correctly!!
|
||||
return s->gpio_state;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gpio_ops = {
|
||||
.read = s5l8900_gpio_read,
|
||||
.write = s5l8900_gpio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_gpio_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchGPIOState *s = IPOD_TOUCH_GPIO(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gpio_ops, s, "gpio", 0x1000);
|
||||
}
|
||||
|
||||
static void s5l8900_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_gpio_info = {
|
||||
.name = TYPE_IPOD_TOUCH_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchGPIOState),
|
||||
.instance_init = s5l8900_gpio_init,
|
||||
.class_init = s5l8900_gpio_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_gpio_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,218 @@
|
||||
#include "hw/arm/ipod_touch_lcd.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/display/framebuffer.h"
|
||||
|
||||
static uint64_t ipod_touch_lcd_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0:
|
||||
return 2;
|
||||
case 0x4:
|
||||
return s->lcd_con;
|
||||
case 0xC:
|
||||
return 0x1; //s->unknown1;
|
||||
case 0x20:
|
||||
return s->w1_display_depth_info;
|
||||
case 0x24:
|
||||
return s->w1_framebuffer_base;
|
||||
case 0x28:
|
||||
return s->w1_hspan;
|
||||
case 0x30:
|
||||
return s->w1_display_resolution_info;
|
||||
case 0x1b10:
|
||||
return 2;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr) {
|
||||
case 0x4:
|
||||
s->lcd_con = val;
|
||||
break;
|
||||
case 0xC:
|
||||
s->unknown1 = val;
|
||||
qemu_irq_lower(s->irq);
|
||||
break;
|
||||
case 0x20:
|
||||
s->w1_display_depth_info = val;
|
||||
break;
|
||||
case 0x24:
|
||||
s->w1_framebuffer_base = val;
|
||||
break;
|
||||
case 0x28:
|
||||
s->w1_hspan = val;
|
||||
break;
|
||||
case 0x30:
|
||||
s->w1_display_resolution_info = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lcd_invalidate(void *opaque)
|
||||
{
|
||||
IPodTouchLCDState *s = opaque;
|
||||
s->invalidate = 1;
|
||||
}
|
||||
|
||||
static void draw_line32_32(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep)
|
||||
{
|
||||
uint8_t r, g, b;
|
||||
|
||||
do {
|
||||
//v = lduw_le_p((void *) s);
|
||||
//printf("V: %d\n", *s);
|
||||
b = s[0];
|
||||
g = s[1];
|
||||
r = s[2];
|
||||
//printf("R: %d, G: %d, B: %d\n", r, g, b);
|
||||
((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
|
||||
s += 4;
|
||||
d += 4;
|
||||
} while (-- width != 0);
|
||||
}
|
||||
|
||||
static void lcd_refresh(void *opaque)
|
||||
{
|
||||
//fprintf(stderr, "%s: refreshing LCD screen\n", __func__);
|
||||
|
||||
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
|
||||
DisplaySurface *surface = qemu_console_surface(lcd->con);
|
||||
drawfn draw_line;
|
||||
int src_width, dest_width;
|
||||
int height, first, last;
|
||||
int width, linesize;
|
||||
|
||||
if (!lcd || !lcd->con || !surface_bits_per_pixel(surface))
|
||||
return;
|
||||
|
||||
dest_width = 4;
|
||||
draw_line = draw_line32_32;
|
||||
|
||||
/* Resolution */
|
||||
first = last = 0;
|
||||
width = 320;
|
||||
height = 480;
|
||||
lcd->invalidate = 1;
|
||||
|
||||
src_width = 4 * width;
|
||||
linesize = surface_stride(surface);
|
||||
|
||||
if(lcd->invalidate) {
|
||||
framebuffer_update_memory_section(&lcd->fbsection, lcd->sysmem, lcd->w1_framebuffer_base, height, 4 * width);
|
||||
}
|
||||
|
||||
framebuffer_update_display(surface, &lcd->fbsection,
|
||||
width, height,
|
||||
src_width, /* Length of source line, in bytes. */
|
||||
linesize, /* Bytes between adjacent horizontal output pixels. */
|
||||
dest_width, /* Bytes between adjacent vertical output pixels. */
|
||||
lcd->invalidate,
|
||||
draw_line, NULL,
|
||||
&first, &last);
|
||||
if (first >= 0) {
|
||||
dpy_gfx_update(lcd->con, 0, first, width, last - first + 1);
|
||||
}
|
||||
lcd->invalidate = 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps lcd_ops = {
|
||||
.read = ipod_touch_lcd_read,
|
||||
.write = ipod_touch_lcd_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const GraphicHwOps gfx_ops = {
|
||||
.invalidate = lcd_invalidate,
|
||||
.gfx_update = lcd_refresh,
|
||||
};
|
||||
|
||||
static void ipod_touch_lcd_mouse_event(void *opaque, int x, int y, int z, int buttons_state)
|
||||
{
|
||||
//printf("CLICKY %d %d %d %d\n", x, y, z, buttons_state);
|
||||
|
||||
// convert x and y to fractional numbers
|
||||
float fx = x / pow(2, 15);
|
||||
float fy = 1 - y / pow(2, 15);
|
||||
|
||||
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
|
||||
lcd->mt->prev_touch_x = lcd->mt->touch_x;
|
||||
lcd->mt->prev_touch_y = lcd->mt->touch_y;
|
||||
lcd->mt->touch_x = fx;
|
||||
lcd->mt->touch_y = fy;
|
||||
|
||||
if(buttons_state && !lcd->mt->touch_down) {
|
||||
ipod_touch_multitouch_on_touch(lcd->mt);
|
||||
}
|
||||
else if(!buttons_state && lcd->mt->touch_down) {
|
||||
ipod_touch_multitouch_on_release(lcd->mt);
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
|
||||
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
|
||||
s->con = graphic_console_init(dev, 0, &gfx_ops, s);
|
||||
qemu_console_resize(s->con, 320, 480);
|
||||
|
||||
// add mouse handler
|
||||
qemu_add_mouse_event_handler(ipod_touch_lcd_mouse_event, s, 1, "iPod Touch Touchscreen");
|
||||
|
||||
// initialize the refresh timer
|
||||
s->refresh_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, refresh_timer_tick, s);
|
||||
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &lcd_ops, s, "lcd", 0x10000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = ipod_touch_lcd_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_lcd_info = {
|
||||
.name = TYPE_IPOD_TOUCH_LCD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchLCDState),
|
||||
.instance_init = ipod_touch_lcd_init,
|
||||
.class_init = ipod_touch_lcd_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_lcd_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "hw/arm/ipod_touch_lis302dl.h"
|
||||
|
||||
static int lis302dl_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t lis302dl_recv(I2CSlave *i2c)
|
||||
{
|
||||
LIS302DLState *s = LIS302DL(i2c);
|
||||
printf("Reading accelerometer register %d\n", s->cmd);
|
||||
|
||||
switch(s->cmd) {
|
||||
case ACCEL_WHOAMI:
|
||||
return ACCEL_WHOAMI_VALUE; // whoami value
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int lis302dl_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
LIS302DLState *s = LIS302DL(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lis302dl_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void lis302dl_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = lis302dl_event;
|
||||
k->recv = lis302dl_recv;
|
||||
k->send = lis302dl_send;
|
||||
}
|
||||
|
||||
static const TypeInfo lis302dl_info = {
|
||||
.name = TYPE_LIS302DL,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = lis302dl_init,
|
||||
.instance_size = sizeof(LIS302DLState),
|
||||
.class_init = lis302dl_class_init,
|
||||
};
|
||||
|
||||
static void lis302dl_register_types(void)
|
||||
{
|
||||
type_register_static(&lis302dl_info);
|
||||
}
|
||||
|
||||
type_init(lis302dl_register_types)
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "hw/arm/ipod_touch_mbx.h"
|
||||
|
||||
static uint32_t reverse_byte_order(uint32_t value) {
|
||||
return ((value & 0x000000FF) << 24) |
|
||||
((value & 0x0000FF00) << 8) |
|
||||
((value & 0x00FF0000) >> 8) |
|
||||
((value & 0xFF000000) >> 24);
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_mbx1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
switch(addr)
|
||||
{
|
||||
case 0x12c:
|
||||
return 0x40;
|
||||
case 0xf00:
|
||||
return (2 << 0x10) | (1 << 0x18); // seems to be some kind of identifier
|
||||
case 0x1020:
|
||||
return 0x10000;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mbx1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
|
||||
static void patch_kernel()
|
||||
{
|
||||
// patch the loading of the AppleBCM4325 driver
|
||||
char *bcm4325_vars = "test";
|
||||
|
||||
// write the pointer to our custom subroutine
|
||||
uint32_t *data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = 0xC0460000;
|
||||
cpu_physical_memory_write(0x8324aa8, (uint8_t *)data, sizeof(uint32_t) * 1);
|
||||
|
||||
// create the call to the subroutine
|
||||
data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = reverse_byte_order(0x0640A0E1); // mov r4, r6
|
||||
data[1] = reverse_byte_order(0x9C309FE5); // ldr r3, [pc, #0x9c]
|
||||
data[2] = reverse_byte_order(0x33FF2FE1); // blx r3
|
||||
data[3] = reverse_byte_order(0x00F020E3); // NOP
|
||||
data[4] = reverse_byte_order(0x00F020E3); // NOP
|
||||
data[5] = reverse_byte_order(0x00F020E3); // NOP
|
||||
cpu_physical_memory_write(0x8324a00, (uint8_t *)data, sizeof(uint32_t) * 6);
|
||||
|
||||
// fill in the driver load subroutine
|
||||
data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = reverse_byte_order(0xFE402DE9); // push on stack
|
||||
|
||||
// TODO I should clean this up
|
||||
for(int i = 1; i < 21; i++) { data[i] = reverse_byte_order(0x00F020E3); } // NOP
|
||||
|
||||
// call the IONetworkController metaclass initialization
|
||||
data[21] = reverse_byte_order(0x0100B0E3); // movs r0, #0x1
|
||||
data[22] = reverse_byte_order(0xB8109FE5); // ldr r1, [pc, #0xb8]
|
||||
data[23] = reverse_byte_order(0xB8209FE5); // ldr r2, [pc, #0xb8]
|
||||
data[24] = reverse_byte_order(0x32FF2FE1); // blx r2
|
||||
|
||||
// load the "com.apple.driver.AppleBCM4325" kext
|
||||
data[25] = reverse_byte_order(0xB4009FE5); // ldr r0, [pc, #0xb4]
|
||||
data[26] = reverse_byte_order(0x0110B0E3); // movs r1, #0x1
|
||||
data[27] = reverse_byte_order(0xB0209FE5); // ldr r2, [pc, #0xd8]
|
||||
data[28] = reverse_byte_order(0x32FF2FE1); // blx r2
|
||||
|
||||
data[29] = reverse_byte_order(0xFE80BDE8); // pop from stack
|
||||
|
||||
cpu_physical_memory_write(0x8460000, (uint8_t *)data, sizeof(uint32_t) * 50);
|
||||
|
||||
// write the data section of the driver load subroutine (0x100 items from the start of the subroutine)
|
||||
data = malloc(sizeof(uint32_t) * 200);
|
||||
data[0] = 0xc0460200; // the address of the BCM4325Vars string
|
||||
data[1] = 0xc013c373; // the address of OSData::withBytes
|
||||
data[2] = 0xc013cc3d; // the address of OSDictionary::withCapacity
|
||||
data[3] = 0xc03467bc; // the "BCM4325Vars" string
|
||||
data[4] = 0xc013ad8d; // the address of OSObject::operator.new
|
||||
data[5] = 0xc032c294; // the object initialization method of AppleBCM4325
|
||||
data[6] = 0xffff; // the 2nd parameter for the call to the IONetworkController metaclass initialization
|
||||
data[7] = 0xc02f94f9; // the initialization method of the IONetworkController metaclass
|
||||
data[8] = 0xc038a320; // the "com.apple.driver.AppleBCM4325" string
|
||||
data[9] = 0xc015de01; // the kmod_load_request method
|
||||
|
||||
cpu_physical_memory_write(0x8460100, (uint8_t *)data, sizeof(uint32_t) * 10);
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_mbx2_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
printf("%s: read from location 0x%08x\n", __func__, addr);
|
||||
switch(addr)
|
||||
{
|
||||
case 0xC:
|
||||
patch_kernel();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mbx2_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchMBXState *s = (IPodTouchMBXState *)opaque;
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_mbx1_ops = {
|
||||
.read = ipod_touch_mbx1_read,
|
||||
.write = ipod_touch_mbx1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps ipod_touch_mbx2_ops = {
|
||||
.read = ipod_touch_mbx2_read,
|
||||
.write = ipod_touch_mbx2_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_mbx_init(Object *obj)
|
||||
{
|
||||
IPodTouchMBXState *s = IPOD_TOUCH_MBX(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem1, obj, &ipod_touch_mbx1_ops, s, TYPE_IPOD_TOUCH_MBX, 0x1000000);
|
||||
sysbus_init_mmio(sbd, &s->iomem1);
|
||||
memory_region_init_io(&s->iomem2, obj, &ipod_touch_mbx2_ops, s, TYPE_IPOD_TOUCH_MBX, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem2);
|
||||
}
|
||||
|
||||
static void ipod_touch_mbx_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_mbx_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MBX,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchMBXState),
|
||||
.instance_init = ipod_touch_mbx_init,
|
||||
.class_init = ipod_touch_mbx_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_mbx_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_mbx_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_mbx_register_types)
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "hw/arm/ipod_touch_mipi_dsi.h"
|
||||
|
||||
static uint64_t ipod_touch_mipi_dsi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchMIPIDSIState *s = (IPodTouchMIPIDSIState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0:
|
||||
return 0x103 | rDSIM_STATUS_TxReadyHsClk;
|
||||
case REG_INTSRC:
|
||||
return rDSIM_INTSRC_RxDatDone;
|
||||
case REG_RXFIFO:
|
||||
if(!s->return_panel_id) {
|
||||
s->return_panel_id = true;
|
||||
// TODO this should be rewritten as a proper queue!
|
||||
return DSIM_RSP_LONG_READ | (3 << 8); // the latter part indicates the length of the response (the panel ID)
|
||||
} else {
|
||||
s->return_panel_id = false;
|
||||
return 0x00a1d13c;
|
||||
}
|
||||
|
||||
case REG_FIFOCTRL:
|
||||
return rDSIM_FIFOCTRL_EmptyHSfr;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mipi_dsi_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchMIPIDSIState *s = (IPodTouchMIPIDSIState *)opaque;
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case REG_PKTHDR:
|
||||
s->pkthdr_reg = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mipi_dsi_ops = {
|
||||
.read = ipod_touch_mipi_dsi_read,
|
||||
.write = ipod_touch_mipi_dsi_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_mipi_dsi_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_mipi_dsi_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchMIPIDSIState *s = IPOD_TOUCH_MIPI_DSI(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &mipi_dsi_ops, s, "mipi_dsi", 0x10000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->return_panel_id = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_mipi_dsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = ipod_touch_mipi_dsi_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_mipi_dsi_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MIPI_DSI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchMIPIDSIState),
|
||||
.instance_init = ipod_touch_mipi_dsi_init,
|
||||
.class_init = ipod_touch_mipi_dsi_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_mipi_dsi_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,414 @@
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
static void prepare_interface_version_response(IPodTouchMultitouchState *s) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// set the interface version
|
||||
s->out_buffer[2] = MT_INTERFACE_VERSION;
|
||||
|
||||
// set the max packet size
|
||||
s->out_buffer[3] = (MT_MAX_PACKET_SIZE & 0xFF);
|
||||
s->out_buffer[4] = (MT_MAX_PACKET_SIZE >> 8) & 0xFF;
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_cmd_status_response(IPodTouchMultitouchState *s) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// TODO we should probably set some CMD status here
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_report_info_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// set the error
|
||||
s->out_buffer[2] = 0;
|
||||
|
||||
// set the report length
|
||||
uint32_t report_length = 0;
|
||||
if(report_id == MT_REPORT_UNKNOWN1) {
|
||||
report_length = MT_REPORT_UNKNOWN1_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_FAMILY_ID) {
|
||||
report_length = MT_REPORT_FAMILY_ID_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
||||
report_length = MT_REPORT_SENSOR_INFO_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
||||
report_length = MT_REPORT_SENSOR_REGION_DESC_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
||||
report_length = MT_REPORT_SENSOR_REGION_PARAM_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
||||
report_length = MT_REPORT_SENSOR_DIMENSIONS_SIZE;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown report ID 0x%02x\n", report_id);
|
||||
}
|
||||
|
||||
s->out_buffer[3] = (report_length & 0xFF);
|
||||
s->out_buffer[4] = (report_length >> 8) & 0xFF;
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_short_control_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
if(report_id == MT_REPORT_FAMILY_ID) {
|
||||
s->out_buffer[3] = MT_FAMILY_ID;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
||||
s->out_buffer[3] = MT_ENDIANNESS;
|
||||
s->out_buffer[4] = MT_SENSOR_ROWS;
|
||||
s->out_buffer[5] = MT_SENSOR_COLUMNS;
|
||||
s->out_buffer[6] = (MT_BCD_VERSION & 0xFF);
|
||||
s->out_buffer[7] = (MT_BCD_VERSION >> 8) & 0xFF;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
||||
s->out_buffer[3] = MT_SENSOR_REGION_DESC;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
||||
s->out_buffer[3] = MT_SENSOR_REGION_PARAM;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
||||
uint32_t *ob_int32 = (uint32_t *)&s->out_buffer[3];
|
||||
ob_int32[0] = MT_SENSOR_SURFACE_WIDTH;
|
||||
ob_int32[1] = MT_SENSOR_SURFACE_HEIGHT;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown report ID 0x%02x\n", report_id);
|
||||
}
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static uint32_t ipod_touch_multitouch_transfer(SSIPeripheral *dev, uint32_t value)
|
||||
{
|
||||
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
|
||||
//printf("<MULTITOUCH> Got value: 0x%02x\n", value);
|
||||
|
||||
if(s->cur_cmd == 0) {
|
||||
//printf("Starting command 0x%02x\n", value);
|
||||
// we're currently not in a command - start a new command
|
||||
s->cur_cmd = value;
|
||||
s->out_buffer = malloc(0x100);
|
||||
s->out_buffer[0] = value; // the response header
|
||||
s->buf_ind = 0;
|
||||
s->in_buffer = malloc(0x100);
|
||||
s->in_buffer_ind = 0;
|
||||
|
||||
if(value == 0x18) { // filler packet??
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0xE1;
|
||||
}
|
||||
else if(value == 0x1A) { // HBPP ACK
|
||||
s->buf_size = 2;
|
||||
if(s->hbpp_atn_ack_response[0] == 0 && s->hbpp_atn_ack_response[1] == 0) {
|
||||
// return the default ACK response
|
||||
s->out_buffer[0] = 0x4B;
|
||||
s->out_buffer[1] = 0xC1;
|
||||
}
|
||||
else {
|
||||
s->out_buffer[0] = s->hbpp_atn_ack_response[0];
|
||||
s->out_buffer[1] = s->hbpp_atn_ack_response[1];
|
||||
}
|
||||
|
||||
}
|
||||
else if(value == 0x1C) { // read register
|
||||
s->buf_size = 8;
|
||||
memset(s->out_buffer, 0, 8); // just return zeros
|
||||
}
|
||||
else if(value == 0x1D) { // execute
|
||||
s->buf_size = 12;
|
||||
memset(s->out_buffer, 0, 12); // just return zeros
|
||||
}
|
||||
else if(value == 0x1F) { // calibration
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0x0;
|
||||
}
|
||||
else if(value == 0x1E) { // write register
|
||||
s->buf_size = 16;
|
||||
memset(s->out_buffer, 0, 16); // just return zeros
|
||||
}
|
||||
else if(value == 0x1F) { // calibration
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0x0;
|
||||
}
|
||||
else if(value == MT_CMD_HBPP_DATA_PACKET) {
|
||||
s->buf_size = 20; // should be enough initially, until we get the packet length
|
||||
memset(s->out_buffer + 1, 0, 20 - 1); // just return zeros
|
||||
}
|
||||
else if(value == 0x47) { // unknown command, probably used to clear the interrupt
|
||||
s->buf_size = 2;
|
||||
}
|
||||
else if(value == MT_CMD_GET_CMD_STATUS) {
|
||||
s->buf_size = 16;
|
||||
prepare_cmd_status_response(s);
|
||||
}
|
||||
else if(value == MT_CMD_GET_INTERFACE_VERSION) {
|
||||
s->buf_size = 16;
|
||||
prepare_interface_version_response(s);
|
||||
}
|
||||
else if(value == MT_CMD_GET_REPORT_INFO) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_SHORT_CONTROL_WRITE) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_SHORT_CONTROL_READ) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_FRAME_READ) {
|
||||
s->buf_size = sizeof(MTFrame);
|
||||
free(s->out_buffer);
|
||||
s->out_buffer = (uint8_t *) s->next_frame;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown command 0x%02x!", value);
|
||||
}
|
||||
}
|
||||
|
||||
s->in_buffer[s->in_buffer_ind] = value;
|
||||
s->in_buffer_ind++;
|
||||
|
||||
if(s->cur_cmd == MT_CMD_HBPP_DATA_PACKET && s->in_buffer_ind == 10) {
|
||||
// verify the header checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 2; i < 8; i++) {
|
||||
checksum += s->in_buffer[i];
|
||||
}
|
||||
|
||||
if(checksum != (s->in_buffer[8] << 8 | s->in_buffer[9])) {
|
||||
hw_error("HBPP data header checksum doesn't match!");
|
||||
}
|
||||
|
||||
uint32_t data_len = (s->in_buffer[2] << 10) | (s->in_buffer[3] << 2) + 5;
|
||||
// extend the lengths of the in/out buffers
|
||||
free(s->in_buffer);
|
||||
s->in_buffer = malloc(data_len + 0x10);
|
||||
|
||||
free(s->out_buffer);
|
||||
s->out_buffer = malloc(data_len);
|
||||
memset(s->out_buffer, 0, data_len);
|
||||
s->buf_size = data_len;
|
||||
s->buf_ind = 0;
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_GET_REPORT_INFO && s->in_buffer_ind == 2) {
|
||||
prepare_report_info_response(s, s->in_buffer[1]);
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_WRITE && s->in_buffer_ind == 16) {
|
||||
// TODO we should persist the report here!
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_READ && s->in_buffer_ind == 2) {
|
||||
prepare_short_control_response(s, s->in_buffer[1]);
|
||||
}
|
||||
|
||||
// TODO process register writes!
|
||||
|
||||
uint8_t ret_val = s->out_buffer[s->buf_ind];
|
||||
s->buf_ind++;
|
||||
|
||||
//printf("<MULTITOUCH> Got value: 0x%02x, returning 0x%02x (index: %d, buffer length: %d)\n", value, ret_val, s->buf_ind, s->buf_size);
|
||||
|
||||
if(s->buf_ind == s->buf_size) {
|
||||
//printf("Finished command 0x%02x\n", s->cur_cmd);
|
||||
|
||||
if(s->cur_cmd == 0x1E) {
|
||||
// make sure we return a success status on the next HBPP ACK
|
||||
s->hbpp_atn_ack_response[0] = 0x4A;
|
||||
s->hbpp_atn_ack_response[1] = 0xD1;
|
||||
}
|
||||
|
||||
// we're done with the command
|
||||
s->cur_cmd = 0;
|
||||
s->buf_size = 0;
|
||||
//free(s->out_buffer);
|
||||
//free(s->in_buffer);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event, float x, float y, uint16_t radius1, uint16_t radius2, uint16_t radius3, uint16_t contactDensity) {
|
||||
MTFrame *frame = calloc(sizeof(MTFrame), sizeof(uint8_t *));
|
||||
|
||||
uint16_t data_len = sizeof(MTFrameHeader) + sizeof(FingerData) + 2;
|
||||
|
||||
/// create the frame length packet
|
||||
frame->frame_length.cmd = MT_CMD_FRAME_READ;
|
||||
frame->frame_length.length1 = (data_len & 0xFF);
|
||||
frame->frame_length.length2 = (data_len >> 8) & 0xFF;
|
||||
|
||||
uint16_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_length)[i];
|
||||
}
|
||||
frame->frame_length.checksum1 = (checksum & 0xFF);
|
||||
frame->frame_length.checksum2 = (checksum >> 8) & 0xFF;
|
||||
|
||||
// create the frame packet
|
||||
frame->frame_packet.cmd = MT_CMD_FRAME_READ;
|
||||
frame->frame_packet.length1 = (data_len & 0xFF);
|
||||
frame->frame_packet.length2 = (data_len >> 8) & 0xFF;
|
||||
|
||||
checksum = 0;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_length)[i];
|
||||
}
|
||||
|
||||
// the first five bytes have to sum up to 0.
|
||||
frame->frame_packet.checksum_pad = 0xFF - (checksum & 0xFF) + 1;
|
||||
|
||||
frame->frame_packet.header.type = MT_FRAME_TYPE_PATH;
|
||||
frame->frame_packet.header.frameNum = s->frame_counter;
|
||||
frame->frame_packet.header.headerLen = sizeof(MTFrameHeader);
|
||||
uint64_t elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 1000000;
|
||||
frame->frame_packet.header.timestamp = elapsed_ns;
|
||||
frame->frame_packet.header.numFingers = 1;
|
||||
frame->frame_packet.header.fingerDataLen = sizeof(FingerData);
|
||||
|
||||
// create the finger data
|
||||
frame->finger_data.id = 1;
|
||||
frame->finger_data.event = event;
|
||||
frame->finger_data.unk_2 = 2;
|
||||
frame->finger_data.unk_3 = 1;
|
||||
|
||||
// compute the velocity
|
||||
int diff_x = (int)((x - s->prev_touch_x) * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
||||
int diff_y = (int)((x - s->prev_touch_y) * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
||||
frame->finger_data.velX = diff_x / (elapsed_ns - s->last_frame_timestamp) * 1000;
|
||||
frame->finger_data.velY = diff_y / (elapsed_ns - s->last_frame_timestamp) * 1000;
|
||||
|
||||
frame->finger_data.x = (int)(x * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
||||
frame->finger_data.y = (int)(y * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
||||
frame->finger_data.radius1 = radius1;
|
||||
frame->finger_data.radius2 = radius2;
|
||||
frame->finger_data.radius3 = radius3;
|
||||
frame->finger_data.angle = 19317;
|
||||
frame->finger_data.contactDensity = contactDensity; // seems to be a medium press
|
||||
|
||||
// compute the checksum over the frame data.
|
||||
checksum = 0;
|
||||
for(int i = 0; i < data_len - 2; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_packet.header)[i];
|
||||
}
|
||||
frame->checksum1 = (checksum & 0xFF);
|
||||
frame->checksum2 = (checksum >> 8) & 0xFF;
|
||||
|
||||
s->last_frame_timestamp = elapsed_ns;
|
||||
s->frame_counter += 1;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_inform_frame_ready(IPodTouchMultitouchState *s) {
|
||||
s->sysic->gpio_int_status[3] |= (1 << 13); // the multitouch interrupt bit is in group 3 (32 interrupts per group), and the 13th of the 3th group
|
||||
qemu_irq_raise(s->sysic->gpio_irqs[3]);
|
||||
}
|
||||
|
||||
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s) {
|
||||
s->touch_down = true;
|
||||
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s) {
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_ENDED, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
||||
s->touch_down = false;
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
timer_del(s->touch_timer);
|
||||
timer_mod(s->touch_end_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
static void touch_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_MOVED, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
if(s->touch_down) {
|
||||
// reschedule the timer
|
||||
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
}
|
||||
|
||||
static void touch_end_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
||||
s->touch_down = false;
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_realize(SSIPeripheral *d, Error **errp)
|
||||
{
|
||||
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(d);
|
||||
memset(s->hbpp_atn_ack_response, 0, 2);
|
||||
s->touch_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_timer_tick, s);
|
||||
s->touch_end_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_end_timer_tick, s);
|
||||
|
||||
s->prev_touch_x = 0;
|
||||
s->prev_touch_y = 0;
|
||||
s->last_frame_timestamp = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
||||
k->realize = ipod_touch_multitouch_realize;
|
||||
k->transfer = ipod_touch_multitouch_transfer;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_multitouch_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MULTITOUCH,
|
||||
.parent = TYPE_SSI_PERIPHERAL,
|
||||
.instance_size = sizeof(IPodTouchMultitouchState),
|
||||
.class_init = ipod_touch_multitouch_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_multitouch_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_multitouch_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_multitouch_register_types)
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "hw/arm/ipod_touch_nor_spi.h"
|
||||
|
||||
static void initialize_nor(IPodTouchNORSPIState *s)
|
||||
{
|
||||
unsigned long fsize;
|
||||
if (g_file_get_contents(s->nor_path, (char **)&s->nor_data, &fsize, NULL)) {
|
||||
s->nor_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ipod_touch_nor_spi_transfer(SSIPeripheral *dev, uint32_t value)
|
||||
{
|
||||
IPodTouchNORSPIState *s = IPOD_TOUCH_NOR_SPI(dev);
|
||||
|
||||
if(s->cur_cmd == NOR_READ_DATA_CMD && s->in_buf_cur_ind == s->in_buf_size && value != 0xFF) {
|
||||
// if we are currently reading from the NOR data and we receive a value that's not the sentinel, reset the current command.
|
||||
s->cur_cmd = 0;
|
||||
}
|
||||
|
||||
if(s->cur_cmd == 0) {
|
||||
// this is a new command -> set it
|
||||
s->cur_cmd = value;
|
||||
s->out_buf = malloc(0x1000);
|
||||
s->in_buf = malloc(0x1000);
|
||||
s->in_buf[0] = value;
|
||||
s->in_buf_size = 0;
|
||||
s->in_buf_cur_ind = 1;
|
||||
s->out_buf_cur_ind = 0;
|
||||
|
||||
if(value == NOR_WRITE_TO_STATUS_REG) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 1;
|
||||
s->out_buf[0] = 0;
|
||||
}
|
||||
else if(value == NOR_GET_STATUS_CMD) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 1;
|
||||
s->out_buf[0] = 0;
|
||||
}
|
||||
else if(value == NOR_WRITE_DATA_CMD) {
|
||||
s->in_buf_size = 4;
|
||||
s->out_buf_size = 256;
|
||||
for(int i = 0; i < 256; i++) { s->out_buf[i] = 0; }; // TODO we ignore this command for now
|
||||
}
|
||||
else if(value == NOR_READ_DATA_CMD) {
|
||||
s->in_buf_size = 4;
|
||||
s->out_buf_size = 4096;
|
||||
}
|
||||
else if(value == NOR_ERASE_BLOCK) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 3;
|
||||
for(int i = 0; i < 3; i++) { s->out_buf[i] = 0x0; } // TODO we ignore this command for now
|
||||
}
|
||||
else if(value == NOR_ENABLE_WRITE) {
|
||||
s->write_enabled = 1;
|
||||
s->cur_cmd = 0;
|
||||
}
|
||||
else if(value == NOR_DISABLE_WRITE) {
|
||||
s->write_enabled = 0;
|
||||
s->cur_cmd = 0;
|
||||
}
|
||||
else if(value == NOR_GET_JEDECID) {
|
||||
s->in_buf_size = 1;
|
||||
s->out_buf_size = 3;
|
||||
s->out_buf[0] = 0x1F; // vendor: atmel, device: 0x02 -> AT25DF081A
|
||||
s->out_buf[1] = 0x45;
|
||||
s->out_buf[2] = 0x02;
|
||||
}
|
||||
else {
|
||||
printf("Unknown command %d!\n", value);
|
||||
hw_error("Unknown command 0x%02x!", value);
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
else if(s->cur_cmd != 0 && s->in_buf_cur_ind < s->in_buf_size) {
|
||||
// we're reading the command
|
||||
s->in_buf[s->in_buf_cur_ind] = value;
|
||||
s->in_buf_cur_ind++;
|
||||
|
||||
if(s->cur_cmd == NOR_GET_STATUS_CMD && s->in_buf_cur_ind == s->in_buf_size) {
|
||||
s->out_buf[0] = 0x0; // indicates that the NOR is ready
|
||||
}
|
||||
else if(s->cur_cmd == NOR_READ_DATA_CMD && s->in_buf_cur_ind == s->in_buf_size) {
|
||||
if(!s->nor_initialized) { initialize_nor(s); }
|
||||
s->nor_read_ind = (s->in_buf[1] << 16) | (s->in_buf[2] << 8) | s->in_buf[3];
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
else {
|
||||
uint8_t ret_val;
|
||||
// otherwise, we're outputting the response
|
||||
if(s->cur_cmd == NOR_READ_DATA_CMD) {
|
||||
uint8_t ret_val = s->nor_data[s->nor_read_ind];
|
||||
s->nor_read_ind++;
|
||||
return ret_val;
|
||||
}
|
||||
else {
|
||||
ret_val = s->out_buf[s->out_buf_cur_ind];
|
||||
s->out_buf_cur_ind++;
|
||||
|
||||
if(s->cur_cmd != 0 && (s->out_buf_cur_ind == s->out_buf_size)) {
|
||||
// the command is done - clean up
|
||||
s->cur_cmd = 0;
|
||||
free(s->in_buf);
|
||||
free(s->out_buf);
|
||||
}
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_nor_spi_realize(SSIPeripheral *d, Error **errp)
|
||||
{
|
||||
IPodTouchNORSPIState *s = IPOD_TOUCH_NOR_SPI(d);
|
||||
s->nor_initialized = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_nor_spi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
||||
k->realize = ipod_touch_nor_spi_realize;
|
||||
k->transfer = ipod_touch_nor_spi_transfer;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_nor_spi_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_NOR_SPI,
|
||||
.parent = TYPE_SSI_PERIPHERAL,
|
||||
.instance_size = sizeof(IPodTouchNORSPIState),
|
||||
.class_init = ipod_touch_nor_spi_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_nor_spi_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_nor_spi_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_nor_spi_register_types)
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "hw/arm/ipod_touch_pcf50633_pmu.h"
|
||||
|
||||
static int pcf50633_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int_to_bcd(int value) {
|
||||
int shift = 0;
|
||||
int res = 0;
|
||||
while (value > 0) {
|
||||
res |= (value % 10) << (shift++ << 2);
|
||||
value /= 10;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t pcf50633_recv(I2CSlave *i2c)
|
||||
{
|
||||
Pcf50633State *s = PCF50633(i2c);
|
||||
printf("Reading PMU register %d\n", s->cmd);
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tm = *localtime(&t);
|
||||
|
||||
int res = 0;
|
||||
|
||||
switch(s->cmd) {
|
||||
case PMU_MBCS1:
|
||||
res = 0; // battery power source
|
||||
break;
|
||||
case PMU_ADCC1:
|
||||
res = 0; // battery charge voltage
|
||||
break;
|
||||
case PMU_RTCSC: // seconds
|
||||
res = int_to_bcd(tm.tm_sec);
|
||||
break;
|
||||
case PMU_RTCMN: // minutes
|
||||
res = int_to_bcd(tm.tm_min);
|
||||
break;
|
||||
case PMU_RTCHR: // hours
|
||||
res = int_to_bcd(tm.tm_hour);
|
||||
break;
|
||||
case PMU_RTCDT: // days
|
||||
res = int_to_bcd(tm.tm_mday);
|
||||
break;
|
||||
case PMU_RTCMT: // month
|
||||
res = int_to_bcd(tm.tm_mon + 1);
|
||||
break;
|
||||
case PMU_RTCYR: // year
|
||||
res = int_to_bcd(tm.tm_year - 100); // the year counts from 1900
|
||||
break;
|
||||
case 0x67:
|
||||
res = 1; // whether we should enable debug UARTS
|
||||
break;
|
||||
case 0x69:
|
||||
res = 0; // boot count error/panic
|
||||
break;
|
||||
case 0x76:
|
||||
res = 0; // unknown register
|
||||
break;
|
||||
default:
|
||||
res = 0;
|
||||
}
|
||||
|
||||
s->cmd += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pcf50633_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
Pcf50633State *s = PCF50633(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcf50633_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void pcf50633_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = pcf50633_event;
|
||||
k->recv = pcf50633_recv;
|
||||
k->send = pcf50633_send;
|
||||
}
|
||||
|
||||
static const TypeInfo pcf50633_info = {
|
||||
.name = TYPE_PCF50633,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = pcf50633_init,
|
||||
.instance_size = sizeof(Pcf50633State),
|
||||
.class_init = pcf50633_class_init,
|
||||
};
|
||||
|
||||
static void pcf50633_register_types(void)
|
||||
{
|
||||
type_register_static(&pcf50633_info);
|
||||
}
|
||||
|
||||
type_init(pcf50633_register_types)
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "hw/arm/ipod_touch_pke.h"
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/bio.h>
|
||||
|
||||
static uint8_t *datahex(char* string) {
|
||||
|
||||
if(string == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t slength = strlen(string);
|
||||
if((slength % 2) != 0) // must be even
|
||||
return NULL;
|
||||
|
||||
size_t dlength = slength / 2;
|
||||
|
||||
uint8_t* data = malloc(dlength);
|
||||
memset(data, 0, dlength);
|
||||
|
||||
size_t index = 0;
|
||||
while (index < slength) {
|
||||
char c = string[index];
|
||||
int value = 0;
|
||||
if(c >= '0' && c <= '9')
|
||||
value = (c - '0');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
value = (10 + (c - 'A'));
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
value = (10 + (c - 'a'));
|
||||
else {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data[(index/2)] += value << (((index + 1) % 2) * 4);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_pke_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchPKEState *s = (IPodTouchPKEState *)opaque;
|
||||
|
||||
//printf("%s: offset 0x%08x\n", __FUNCTION__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case REG_PKE_SEG_SIZE:
|
||||
return s->seg_size_reg;
|
||||
case REG_PKE_SEG_START ... (REG_PKE_SEG_START + 1024):
|
||||
{
|
||||
uint32_t *res = (uint32_t *)s->segments;
|
||||
return res[(offset - REG_PKE_SEG_START) / 4];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_pke_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchPKEState *s = (IPodTouchPKEState *)opaque;
|
||||
|
||||
//printf("%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case 0x0:
|
||||
s->num_started = 0;
|
||||
break;
|
||||
case 0x10:
|
||||
break;
|
||||
case REG_PKE_SEG_START ... (REG_PKE_SEG_START + 1024):
|
||||
{
|
||||
uint32_t *segments_cast = (uint32_t *)s->segments;
|
||||
segments_cast[(offset - REG_PKE_SEG_START) / 4] = value;
|
||||
break;
|
||||
}
|
||||
case REG_PKE_START:
|
||||
{
|
||||
s->num_started++;
|
||||
|
||||
if(s->num_started == 5) { // TODO this is arbitrary!
|
||||
|
||||
|
||||
BIGNUM *mod_bn = BN_lebin2bn(s->segments, s->segment_size, NULL);
|
||||
BIGNUM *base_bn = BN_lebin2bn(s->segments + s->segment_size, s->segment_size, NULL);
|
||||
BIGNUM *exp_bn = BN_new();
|
||||
BN_dec2bn(&exp_bn,"65537");
|
||||
// BN_print(BIO_new_fp(stdout, BIO_NOCLOSE), exp_bn);
|
||||
// printf("\n\n");
|
||||
|
||||
BIGNUM *res = BN_new();
|
||||
BN_CTX *ctx = BN_CTX_new();
|
||||
BN_mod_exp(res, base_bn, exp_bn, mod_bn, ctx);
|
||||
// BN_print(BIO_new_fp(stdout, BIO_NOCLOSE), res);
|
||||
// printf("\n\n");
|
||||
|
||||
char *res_hex = datahex(BN_bn2hex(res));
|
||||
|
||||
// copy this into SEG1 - note that the hex conversion removes the first 0x00 bytes so we add it back and shift everything to the right one place.
|
||||
for(int i = 0; i < (s->segment_size - 1); i++) { s->segments[s->segment_size + s->segment_size - 2 - i] = res_hex[i]; }
|
||||
s->segments[s->segment_size + s->segment_size - 1] = 0x0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case REG_PKE_SEG_SIZE:
|
||||
s->seg_size_reg = value;
|
||||
uint32_t size_bit = (s->seg_size_reg >> 6);
|
||||
if(size_bit == 0) { s->segment_size = 256; }
|
||||
else if(size_bit == 1) { s->segment_size = 128; }
|
||||
else { }
|
||||
break;
|
||||
case REG_PKE_SWRESET:
|
||||
s->num_started = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pke_ops = {
|
||||
.read = ipod_touch_pke_read,
|
||||
.write = ipod_touch_pke_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_pke_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchPKEState *s = IPOD_TOUCH_PKE(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &pke_ops, s, "pke", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_pke_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_pke_info = {
|
||||
.name = TYPE_IPOD_TOUCH_PKE,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchPKEState),
|
||||
.instance_init = ipod_touch_pke_init,
|
||||
.class_init = ipod_touch_pke_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_pke_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "hw/arm/ipod_touch_scaler_csc.h"
|
||||
|
||||
static uint64_t ipod_touch_scaler_csc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_scaler_csc_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||
{
|
||||
IPodTouchScalerCSCState *s = IPOD_TOUCH_SCALER_CSC(opaque);
|
||||
//printf("%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_scaler_csc_ops = {
|
||||
.read = ipod_touch_scaler_csc_read,
|
||||
.write = ipod_touch_scaler_csc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_scaler_csc_init(Object *obj)
|
||||
{
|
||||
IPodTouchScalerCSCState *s = IPOD_TOUCH_SCALER_CSC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_scaler_csc_ops, s, TYPE_IPOD_TOUCH_SCALER_CSC, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_scaler_csc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_scaler_csc_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SCALER_CSC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchScalerCSCState),
|
||||
.instance_init = ipod_touch_scaler_csc_init,
|
||||
.class_init = ipod_touch_scaler_csc_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_scaler_csc_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_scaler_csc_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_scaler_csc_register_types)
|
||||
@@ -0,0 +1,252 @@
|
||||
#include "hw/arm/ipod_touch_sdio.h"
|
||||
|
||||
static void trigger_irq(void *opaque)
|
||||
{
|
||||
IPodTouchSDIOState *s = (IPodTouchSDIOState *)opaque;
|
||||
s->irq_reg = 0x2;
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
|
||||
void sdio_exec_cmd(IPodTouchSDIOState *s)
|
||||
{
|
||||
uint32_t cmd_type = s->cmd & 0x3f;
|
||||
uint32_t addr = (s->arg >> 9) & 0x1ffff;
|
||||
uint32_t func = (s->arg >> 28) & 0x7;
|
||||
//printf("SDIO CMD: %d, ADDR: %d, FUNC: %d\n", cmd_type, addr, func);
|
||||
if(cmd_type == 0x3) {
|
||||
// RCA request - ignore
|
||||
}
|
||||
else if(cmd_type == 0x5) {
|
||||
if(addr == 0) {
|
||||
// reading slot 0 - make sure there is a device here
|
||||
//s->resp0 = (1 << 31) /* indicate ready */ | (BCM4325_FUNCTIONS << CMD5_FUNC_OFFSET) /* number of functions */;
|
||||
}
|
||||
}
|
||||
else if(cmd_type == 0x7) {
|
||||
// select card - ignore
|
||||
}
|
||||
else if(cmd_type == 0x34) {
|
||||
// CMD52 - read/write from a register
|
||||
bool is_write = (s->arg >> 31) != 0;
|
||||
if(is_write) {
|
||||
uint8_t data = s->arg & 0xFF;
|
||||
s->registers[addr] = data;
|
||||
if(addr == 0x2) { s->registers[0x3] = data; } // if we write to register 2, we also write the same result to register 3 (this is the enabled functions register)
|
||||
printf("SDIO: Executing cmd52 by writing 0x%02x to register 0x%02x\n", data, addr);
|
||||
} else {
|
||||
if(addr == 0x1000e) {
|
||||
// misc register
|
||||
s->resp0 = (1 << 6) /* enable ALP clock */ | (1 << 7); /* enable HT clock */
|
||||
}
|
||||
else if(addr == 0x2020) {
|
||||
// some indication that packets are ready??
|
||||
s->resp0 = (1 << 6);
|
||||
}
|
||||
else {
|
||||
printf("SDIO: Executing cmd52 by reading from 0x%02x (value: 0x%02x)\n", addr, s->registers[addr]);
|
||||
s->resp0 = s->registers[addr];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(cmd_type == 0x35) {
|
||||
// CMD53 - block transfer
|
||||
addr = addr & 0x7fff;
|
||||
bool is_write = (s->arg >> 31) != 0;
|
||||
printf("SDIO: Executing cmd53 with block size %d and %d blocks (reg address: 0x%08x, destination address: 0x%08x, write? %d)\n", s->blklen, s->numblk, addr, s->baddr, is_write);
|
||||
|
||||
if(is_write) {
|
||||
if(func == 0x1) {
|
||||
cpu_physical_memory_read(s->baddr, &s->registers[addr], s->blklen * s->numblk);
|
||||
}
|
||||
else if(func == 0x2) {
|
||||
// this is a BCM4325 command - add a frame to the queue and schedule the IRQ request to indicate that the command has been completed
|
||||
BCM4325FrameHeaderPacket *frame_header = calloc(sizeof(BCM4325FrameHeaderPacket), sizeof(uint8_t *));
|
||||
uint16_t length = 14;
|
||||
frame_header->frame_length = length;
|
||||
frame_header->checksum = length ^ 0xffff;
|
||||
g_queue_push_tail(s->rx_fifo, frame_header);
|
||||
|
||||
timer_mod(s->irq_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 100);
|
||||
}
|
||||
} else {
|
||||
if(func == 0x1) {
|
||||
if(addr == 0x0) {
|
||||
// chip ID register
|
||||
uint32_t chipid[] = { 0x5 << 0x10 };
|
||||
cpu_physical_memory_write(s->baddr, &chipid, 0x4);
|
||||
}
|
||||
}
|
||||
else if(func == 0x2) {
|
||||
// we're reading a frame
|
||||
BCM4325FrameHeaderPacket *frame_header = (BCM4325FrameHeaderPacket *) g_queue_pop_head(s->rx_fifo);
|
||||
|
||||
if(!frame_header) {
|
||||
// create an empty frame
|
||||
frame_header = calloc(sizeof(BCM4325FrameHeaderPacket), sizeof(uint8_t *));
|
||||
}
|
||||
cpu_physical_memory_write(s->baddr, frame_header, sizeof(BCM4325FrameHeaderPacket));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// toggle IRQ register
|
||||
s->irq_reg = 0x1;
|
||||
qemu_irq_raise(s->irq);
|
||||
//printf("Raised IRQ\n");
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown SDIO command %d", cmd_type);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_sdio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
|
||||
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case SDIO_CMD:
|
||||
s->cmd = value;
|
||||
if(value & (1 << 31)) { // execute bit is set
|
||||
sdio_exec_cmd(s);
|
||||
}
|
||||
break;
|
||||
case SDIO_ARGU:
|
||||
s->arg = value;
|
||||
break;
|
||||
case SDIO_STATE:
|
||||
s->state = value;
|
||||
break;
|
||||
case SDIO_STAC:
|
||||
s->stac = value;
|
||||
break;
|
||||
case SDIO_CSR:
|
||||
s->csr = value;
|
||||
break;
|
||||
case SDIO_IRQ:
|
||||
qemu_irq_lower(s->irq);
|
||||
break;
|
||||
case SDIO_IRQMASK:
|
||||
s->irq_mask = value;
|
||||
break;
|
||||
case SDIO_BADDR:
|
||||
s->baddr = value;
|
||||
break;
|
||||
case SDIO_BLKLEN:
|
||||
s->blklen = value;
|
||||
break;
|
||||
case SDIO_NUMBLK:
|
||||
s->numblk = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_sdio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//printf("%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case SDIO_CMD:
|
||||
return s->cmd;
|
||||
case SDIO_ARGU:
|
||||
return s->arg;
|
||||
case SDIO_STATE:
|
||||
return s->state;
|
||||
case SDIO_STAC:
|
||||
return s->stac;
|
||||
case SDIO_DSTA:
|
||||
return (1 << 0) | (1 << 4) ; // 0x1 indicates that the SDIO is ready for a CMD, (1 << 4) that the command is complete
|
||||
case SDIO_RESP0:
|
||||
return s->resp0;
|
||||
case SDIO_RESP1:
|
||||
return s->resp1;
|
||||
case SDIO_RESP2:
|
||||
return s->resp2;
|
||||
case SDIO_RESP3:
|
||||
return s->resp3;
|
||||
case SDIO_CSR:
|
||||
return s->csr;
|
||||
case SDIO_IRQ:
|
||||
return s->irq_reg;
|
||||
case SDIO_IRQMASK:
|
||||
return s->irq_mask;
|
||||
case SDIO_BADDR:
|
||||
return s->baddr;
|
||||
case SDIO_BLKLEN:
|
||||
return s->blklen;
|
||||
case SDIO_NUMBLK:
|
||||
return s->numblk;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_sdio_ops = {
|
||||
.read = ipod_touch_sdio_read,
|
||||
.write = ipod_touch_sdio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sdio_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchSDIOState *s = IPOD_TOUCH_SDIO(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
s->registers[0x9] = CIS_OFFSET; // registers 0x9 - 0xB contain the relative address offset, which we set to 0xC8 (200)
|
||||
s->registers[19] = 0x1; // enable support for high speed mode
|
||||
|
||||
// set the vendor information
|
||||
s->registers[CIS_OFFSET] = CIS_MANUFACTURER_ID;
|
||||
s->registers[CIS_OFFSET + 1] = 0x4;
|
||||
s->registers[CIS_OFFSET + 2] = BCM4325_MANUFACTURER & 0xFF;
|
||||
s->registers[CIS_OFFSET + 3] = (BCM4325_MANUFACTURER >> 8) & 0xFF;
|
||||
s->registers[CIS_OFFSET + 4] = BCM4325_PRODUCT_ID & 0xFF;
|
||||
s->registers[CIS_OFFSET + 5] = (BCM4325_PRODUCT_ID >> 8) & 0xFF;
|
||||
|
||||
// set the MAC address (00:23:32:6E:AA:10)
|
||||
s->registers[CIS_OFFSET + 6] = CIS_FUNCTION_EXTENSION;
|
||||
s->registers[CIS_OFFSET + 8] = 0x4; // unknown
|
||||
s->registers[CIS_OFFSET + 9] = 0x6; // the length of the MAC address
|
||||
s->registers[CIS_OFFSET + 10] = 0x0;
|
||||
s->registers[CIS_OFFSET + 11] = 0x23;
|
||||
s->registers[CIS_OFFSET + 12] = 0x32;
|
||||
s->registers[CIS_OFFSET + 13] = 0x6E;
|
||||
s->registers[CIS_OFFSET + 14] = 0xAA;
|
||||
s->registers[CIS_OFFSET + 15] = 0x10;
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_sdio_ops, s, TYPE_IPOD_TOUCH_SDIO, 4096);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->irq_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, trigger_irq, s);
|
||||
|
||||
s->rx_fifo = g_queue_new();
|
||||
}
|
||||
|
||||
static void ipod_touch_sdio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sdio_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SDIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSDIOState),
|
||||
.instance_init = ipod_touch_sdio_init,
|
||||
.class_init = ipod_touch_sdio_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_sdio_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sdio_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_sdio_register_types)
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "hw/arm/ipod_touch_sha1.h"
|
||||
|
||||
static uint64_t swapLong(void *X) {
|
||||
uint64_t x = (uint64_t) X;
|
||||
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
|
||||
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
|
||||
x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
|
||||
return x;
|
||||
}
|
||||
|
||||
static void flush_hw_buffer(IPodTouchSHA1State *s) {
|
||||
// Flush the hardware buffer to the state buffer and clear the buffer.
|
||||
memcpy(s->buffer + s->buffer_ind, (uint8_t *)s->hw_buffer, 0x40);
|
||||
memset(s->hw_buffer, 0, 0x40);
|
||||
s->hw_buffer_dirty = false;
|
||||
s->buffer_ind += 0x40;
|
||||
}
|
||||
|
||||
static void sha1_reset(IPodTouchSHA1State *s)
|
||||
{
|
||||
s->config = 0;
|
||||
s->memory_start = 0;
|
||||
s->memory_mode = 0;
|
||||
s->insize = 0;
|
||||
memset(&s->buffer, 0, SHA1_BUFFER_SIZE);
|
||||
memset(&s->hw_buffer, 0, 0x10 * sizeof(uint32_t));
|
||||
s->buffer_ind = 0;
|
||||
memset(&s->hashout, 0, 0x14);
|
||||
s->hw_buffer_dirty = false;
|
||||
s->hash_computed = false;
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_sha1_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchSHA1State *s = (IPodTouchSHA1State *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset 0x%08x\n", __FUNCTION__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SHA_CONFIG:
|
||||
return s->config;
|
||||
case SHA_RESET:
|
||||
return 0;
|
||||
case SHA_MEMORY_START:
|
||||
return s->memory_start;
|
||||
case SHA_MEMORY_MODE:
|
||||
return s->memory_mode;
|
||||
case SHA_INSIZE:
|
||||
return s->insize;
|
||||
/* Hash result ouput */
|
||||
case 0x20 ... 0x34:
|
||||
//fprintf(stderr, "Hash out %08x\n", *(uint32_t *)&s->hashout[offset - 0x20]);
|
||||
if(!s->hash_computed) {
|
||||
// lazy compute the final hash by inspecting the last eight bytes of the buffer, which contains the length of the input data.
|
||||
uint64_t data_length = swapLong(((uint64_t *)s->buffer)[s->buffer_ind / 8 - 1]) / 8;
|
||||
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, s->buffer, data_length);
|
||||
SHA1_Final(s->hashout, &ctx);
|
||||
s->hash_computed = true;
|
||||
}
|
||||
|
||||
return *(uint32_t *)&s->hashout[offset - 0x20];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_sha1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchSHA1State *s = (IPodTouchSHA1State *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case SHA_CONFIG:
|
||||
if(value == 0x2 || value == 0xa)
|
||||
{
|
||||
if(s->hw_buffer_dirty) {
|
||||
flush_hw_buffer(s);
|
||||
}
|
||||
|
||||
if(s->memory_mode)
|
||||
{
|
||||
// we are in memory mode - gradually add the memory to the buffer
|
||||
for(int i = 0; i < s->insize / 0x40; i++) {
|
||||
cpu_physical_memory_read(s->memory_start + i * 0x40, s->buffer + s->buffer_ind, 0x40);
|
||||
s->buffer_ind += 0x40;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->config = value;
|
||||
}
|
||||
break;
|
||||
case SHA_RESET:
|
||||
sha1_reset(s);
|
||||
break;
|
||||
case SHA_MEMORY_START:
|
||||
s->memory_start = value;
|
||||
break;
|
||||
case SHA_MEMORY_MODE:
|
||||
s->memory_mode = value;
|
||||
break;
|
||||
case SHA_INSIZE:
|
||||
assert(value <= SHA1_BUFFER_SIZE);
|
||||
s->insize = value;
|
||||
break;
|
||||
case 0x40 ... 0x7c:
|
||||
// write to the hardware buffer
|
||||
s->hw_buffer[(offset - 0x40) / 4] |= value;
|
||||
s->hw_buffer_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sha1_ops = {
|
||||
.read = ipod_touch_sha1_read,
|
||||
.write = ipod_touch_sha1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sha1_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchSHA1State *s = IPOD_TOUCH_SHA1(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &sha1_ops, s, "sha1", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
sha1_reset(s);
|
||||
}
|
||||
|
||||
static void ipod_touch_sha1_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sha1_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SHA1,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSHA1State),
|
||||
.instance_init = ipod_touch_sha1_init,
|
||||
.class_init = ipod_touch_sha1_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sha1_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -4,9 +4,9 @@
|
||||
* by cmw
|
||||
*/
|
||||
|
||||
#include "hw/arm/atv4_spi.h"
|
||||
#include "hw/arm/ipod_touch_spi.h"
|
||||
|
||||
static int apple_spi_word_size(ATV4SPIState *s)
|
||||
static int apple_spi_word_size(IPodTouchSPIState *s)
|
||||
{
|
||||
switch (R_CFG_WORD_SIZE(REG(s, R_CFG))) {
|
||||
case R_CFG_WORD_SIZE_8B:
|
||||
@@ -21,21 +21,21 @@ static int apple_spi_word_size(ATV4SPIState *s)
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void apple_spi_update_xfer_tx(ATV4SPIState *s)
|
||||
static void apple_spi_update_xfer_tx(IPodTouchSPIState *s)
|
||||
{
|
||||
if (fifo8_is_empty(&s->tx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_TXEMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_xfer_rx(ATV4SPIState *s)
|
||||
static void apple_spi_update_xfer_rx(IPodTouchSPIState *s)
|
||||
{
|
||||
if (!fifo8_is_empty(&s->rx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_RXREADY;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_irq(ATV4SPIState *s)
|
||||
static void apple_spi_update_irq(IPodTouchSPIState *s)
|
||||
{
|
||||
uint32_t irq = 0;
|
||||
uint32_t mask = 0;
|
||||
@@ -59,7 +59,7 @@ static void apple_spi_update_irq(ATV4SPIState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_cs(ATV4SPIState *s)
|
||||
static void apple_spi_update_cs(IPodTouchSPIState *s)
|
||||
{
|
||||
BusState *b = BUS(s->spi);
|
||||
BusChild *kid = QTAILQ_FIRST(&b->children);
|
||||
@@ -71,7 +71,7 @@ static void apple_spi_update_cs(ATV4SPIState *s)
|
||||
|
||||
static void apple_spi_cs_set(void *opaque, int pin, int level)
|
||||
{
|
||||
ATV4SPIState *s = ATV4_SPI(opaque);
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
|
||||
if (level) {
|
||||
REG(s, R_PIN) |= R_PIN_CS;
|
||||
} else {
|
||||
@@ -80,7 +80,7 @@ static void apple_spi_cs_set(void *opaque, int pin, int level)
|
||||
apple_spi_update_cs(s);
|
||||
}
|
||||
|
||||
static void apple_spi_run(ATV4SPIState *s)
|
||||
static void apple_spi_run(IPodTouchSPIState *s)
|
||||
{
|
||||
uint32_t tx;
|
||||
uint32_t rx;
|
||||
@@ -136,9 +136,9 @@ static void apple_spi_run(ATV4SPIState *s)
|
||||
//printf("<after> TX buffer size: %d, RX buffer size: %d\n", fifo8_num_used(&s->tx_fifo), fifo8_num_used(&s->rx_fifo));
|
||||
}
|
||||
|
||||
static uint64_t atv4_spi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
static uint64_t ipod_touch_spi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ATV4SPIState *s = ATV4_SPI(opaque);
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
|
||||
//printf("%s (base %d): read from location 0x%08x\n", __func__, s->base, addr);
|
||||
|
||||
uint32_t r;
|
||||
@@ -182,9 +182,9 @@ static uint64_t atv4_spi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
return r;
|
||||
}
|
||||
|
||||
static void atv4_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||
static void ipod_touch_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||
{
|
||||
ATV4SPIState *s = ATV4_SPI(opaque);
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
|
||||
//printf("%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
|
||||
|
||||
uint32_t r = data;
|
||||
@@ -244,14 +244,14 @@ static void atv4_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned si
|
||||
}
|
||||
|
||||
static const MemoryRegionOps spi_ops = {
|
||||
.read = atv4_spi_read,
|
||||
.write = atv4_spi_write,
|
||||
.read = ipod_touch_spi_read,
|
||||
.write = ipod_touch_spi_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void atv4_spi_reset(DeviceState *d)
|
||||
static void ipod_touch_spi_reset(DeviceState *d)
|
||||
{
|
||||
ATV4SPIState *s = (ATV4SPIState *)d;
|
||||
IPodTouchSPIState *s = (IPodTouchSPIState *)d;
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
@@ -264,9 +264,9 @@ void set_spi_base(uint32_t base)
|
||||
base_addr = base;
|
||||
}
|
||||
|
||||
static void atv4_spi_realize(DeviceState *dev, struct Error **errp)
|
||||
static void ipod_touch_spi_realize(DeviceState *dev, struct Error **errp)
|
||||
{
|
||||
ATV4SPIState *s = ATV4_SPI(dev);
|
||||
IPodTouchSPIState *s = IPOD_TOUCH_SPI(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
char bus_name[32] = { 0 };
|
||||
@@ -285,44 +285,44 @@ static void atv4_spi_realize(DeviceState *dev, struct Error **errp)
|
||||
fifo8_create(&s->tx_fifo, R_FIFO_TX_DEPTH);
|
||||
fifo8_create(&s->rx_fifo, R_FIFO_RX_DEPTH);
|
||||
|
||||
// // create the peripheral
|
||||
// switch(s->base) {
|
||||
// case 0:
|
||||
// {
|
||||
// DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_NOR_SPI);
|
||||
// IPodTouchNORSPIState *nor = IPOD_TOUCH_NOR_SPI(dev);
|
||||
// s->nor = nor;
|
||||
// break;
|
||||
// }
|
||||
// case 1:
|
||||
// break;
|
||||
// case 4:
|
||||
// {
|
||||
// DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_MULTITOUCH);
|
||||
// IPodTouchMultitouchState *mt = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
// s->mt = mt;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// create the peripheral
|
||||
switch(s->base) {
|
||||
case 0:
|
||||
{
|
||||
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_NOR_SPI);
|
||||
IPodTouchNORSPIState *nor = IPOD_TOUCH_NOR_SPI(dev);
|
||||
s->nor = nor;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_MULTITOUCH);
|
||||
IPodTouchMultitouchState *mt = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
s->mt = mt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void atv4_spi_class_init(ObjectClass *klass, void *data)
|
||||
static void ipod_touch_spi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = atv4_spi_realize;
|
||||
dc->reset = atv4_spi_reset;
|
||||
dc->realize = ipod_touch_spi_realize;
|
||||
dc->reset = ipod_touch_spi_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo atv4_spi_info = {
|
||||
.name = TYPE_ATV4_SPI,
|
||||
static const TypeInfo ipod_touch_spi_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SPI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ATV4SPIState),
|
||||
.class_init = atv4_spi_class_init,
|
||||
.instance_size = sizeof(IPodTouchSPIState),
|
||||
.class_init = ipod_touch_spi_class_init,
|
||||
};
|
||||
|
||||
static void atv4_spi_register_types(void)
|
||||
static void ipod_touch_spi_register_types(void)
|
||||
{
|
||||
type_register_static(&atv4_spi_info);
|
||||
type_register_static(&ipod_touch_spi_info);
|
||||
}
|
||||
|
||||
type_init(atv4_spi_register_types)
|
||||
type_init(ipod_touch_spi_register_types)
|
||||
@@ -0,0 +1,129 @@
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
|
||||
static uint64_t ipod_touch_sysic_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case POWER_ID:
|
||||
return s->power_id;
|
||||
case POWER_SETSTATE:
|
||||
case POWER_STATE:
|
||||
return s->power_state;
|
||||
case 0x7a:
|
||||
case 0x7c:
|
||||
return 1;
|
||||
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTLEVEL) / 4;
|
||||
return s->gpio_int_level[group];
|
||||
}
|
||||
case GPIO_INTSTAT ... (GPIO_INTSTAT + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTSTAT) / 4;
|
||||
return s->gpio_int_status[group];
|
||||
}
|
||||
case GPIO_INTEN ... (GPIO_INTEN + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTEN) / 4;
|
||||
return s->gpio_int_enabled[group];
|
||||
}
|
||||
case GPIO_INTTYPE ... (GPIO_INTTYPE + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTTYPE) / 4;
|
||||
return s->gpio_int_type[group];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_sysic_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
|
||||
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch (addr) {
|
||||
case POWER_ID:
|
||||
s->power_id = val;
|
||||
break;
|
||||
case POWER_ONCTRL:
|
||||
if((val & 0x20) != 0 || (val & 0x4) != 0 || (val & POWER_ID_ADM) != 0) { break; } // make sure that we do not record the 'on' state of some devices so it appears like they are turned on immediately.
|
||||
s->power_state = val;
|
||||
break;
|
||||
case POWER_OFFCTRL:
|
||||
s->power_state = val;
|
||||
break;
|
||||
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
break;
|
||||
}
|
||||
case GPIO_INTSTAT ... (GPIO_INTSTAT + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTSTAT) / 4;
|
||||
|
||||
// acknowledge the interrupts and clear the corresponding bits
|
||||
s->gpio_int_status[group] = s->gpio_int_status[group] & ~val;
|
||||
|
||||
qemu_irq_lower(s->gpio_irqs[group]);
|
||||
|
||||
break;
|
||||
}
|
||||
case GPIO_INTEN ... (GPIO_INTEN + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTEN) / 4;
|
||||
s->gpio_int_enabled[group] = val;
|
||||
break;
|
||||
}
|
||||
case GPIO_INTTYPE ... (GPIO_INTTYPE + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTTYPE) / 4;
|
||||
s->gpio_int_type[group] = val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_sysic_ops = {
|
||||
.read = ipod_touch_sysic_read,
|
||||
.write = ipod_touch_sysic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sysic_init(Object *obj)
|
||||
{
|
||||
IPodTouchSYSICState *s = IPOD_TOUCH_SYSIC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_sysic_ops, s, TYPE_IPOD_TOUCH_SYSIC, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
|
||||
sysbus_init_irq(sbd, &s->gpio_irqs[grp]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_sysic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sysic_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SYSIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSYSICState),
|
||||
.instance_init = ipod_touch_sysic_init,
|
||||
.class_init = ipod_touch_sysic_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_sysic_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sysic_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_sysic_register_types)
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "hw/arm/ipod_touch_timer.h"
|
||||
|
||||
static void s5l8900_st_update(IPodTouchTimerState *s)
|
||||
{
|
||||
s->freq_out = 1000000000 / 100;
|
||||
s->tick_interval = /* bcount1 * get_ticks / freq + ((bcount2 * get_ticks / freq)*/
|
||||
muldiv64((s->bcount1 < 1000) ? 1000 : s->bcount1, NANOSECONDS_PER_SECOND, s->freq_out);
|
||||
s->next_planned_tick = 0;
|
||||
}
|
||||
|
||||
static void s5l8900_st_set_timer(IPodTouchTimerState *s)
|
||||
{
|
||||
uint64_t last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->base_time;
|
||||
|
||||
s->next_planned_tick = last + (s->tick_interval - last % s->tick_interval);
|
||||
timer_mod(s->st_timer, s->next_planned_tick + s->base_time);
|
||||
s->last_tick = last;
|
||||
}
|
||||
|
||||
static void s5l8900_st_tick(void *opaque)
|
||||
{
|
||||
IPodTouchTimerState *s = (IPodTouchTimerState *)opaque;
|
||||
|
||||
if (s->status & TIMER_STATE_START) {
|
||||
//fprintf(stderr, "%s: Raising irq\n", __func__);
|
||||
qemu_irq_raise(s->irq);
|
||||
|
||||
/* schedule next interrupt */
|
||||
if(!(s->status & TIMER_STATE_MANUALUPDATE)) {
|
||||
s5l8900_st_set_timer(s);
|
||||
}
|
||||
} else {
|
||||
s->next_planned_tick = 0;
|
||||
s->last_tick = 0;
|
||||
timer_del(s->st_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void s5l8900_timer1_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
|
||||
|
||||
switch(addr){
|
||||
|
||||
case TIMER_IRQSTAT:
|
||||
s->irqstat = value;
|
||||
return;
|
||||
case TIMER_IRQLATCH:
|
||||
//fprintf(stderr, "%s: lowering irq\n", __func__);
|
||||
qemu_irq_lower(s->irq);
|
||||
return;
|
||||
case TIMER_4 + TIMER_CONFIG:
|
||||
s5l8900_st_update(s);
|
||||
s->config = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_STATE:
|
||||
if (value & TIMER_STATE_START) {
|
||||
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s5l8900_st_update(s);
|
||||
s5l8900_st_set_timer(s);
|
||||
} else if (value == TIMER_STATE_STOP) {
|
||||
timer_del(s->st_timer);
|
||||
}
|
||||
s->status = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_COUNT_BUFFER:
|
||||
s->bcount1 = s->bcreload = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_COUNT_BUFFER2:
|
||||
s->bcount2 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_timer1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
|
||||
uint64_t elapsed_ns, ticks;
|
||||
|
||||
switch (addr) {
|
||||
case TIMER_TICKSHIGH: // needs to be fixed so that read from low first works as well
|
||||
|
||||
elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 2; // the timer ticks twice as slow as the CPU frequency in the kernel
|
||||
ticks = clock_ns_to_ticks(s->sysclk, elapsed_ns);
|
||||
//printf("TICKS: %lld\n", ticks);
|
||||
s->ticks_high = (ticks >> 32);
|
||||
s->ticks_low = (ticks & 0xFFFFFFFF);
|
||||
return s->ticks_high;
|
||||
case TIMER_TICKSLOW:
|
||||
return s->ticks_low;
|
||||
case TIMER_IRQSTAT:
|
||||
return ~0; // s->irqstat;
|
||||
case TIMER_IRQLATCH:
|
||||
return 0xffffffff;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps timer1_ops = {
|
||||
.read = s5l8900_timer1_read,
|
||||
.write = s5l8900_timer1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_timer_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchTimerState *s = IPOD_TOUCH_TIMER(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &timer1_ops, s, "timer1", 0x10001);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->st_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s5l8900_st_tick, s);
|
||||
}
|
||||
|
||||
static void s5l8900_timer_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_timer_info = {
|
||||
.name = TYPE_IPOD_TOUCH_TIMER,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchTimerState),
|
||||
.instance_init = s5l8900_timer_init,
|
||||
.class_init = s5l8900_timer_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_timer_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "hw/arm/ipod_touch_tvout.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static uint64_t ipod_touch_tvout_sdo_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: offset = 0x%08x\n", __func__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SDO_CLKCON:
|
||||
return s->sdo_clkcon;
|
||||
case SDO_CONFIG:
|
||||
return s->sdo_config;
|
||||
case SDO_IRQ:
|
||||
return s->sdo_irq;
|
||||
case SDO_IRQMASK:
|
||||
return s->sdo_irq_mask;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_sdo_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SDO_CLKCON:
|
||||
s->sdo_clkcon = value;
|
||||
return;
|
||||
case SDO_CONFIG:
|
||||
s->sdo_config = value;
|
||||
return;
|
||||
case SDO_IRQ:
|
||||
s->sdo_irq = 0x0;
|
||||
qemu_irq_lower(s->irq);
|
||||
return;
|
||||
case SDO_IRQMASK:
|
||||
s->sdo_irq_mask = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_tvout_mixer1_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: offset = 0x%08x\n", __func__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
return 0x4;
|
||||
case MXR_CFG:
|
||||
return s->mixer1_cfg;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_mixer1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
if((value & 0x1) && (s->sdo_irq_mask & 1) == 0 && s->irq_count < 2) {
|
||||
s->sdo_irq = 0x1;
|
||||
s->irq_count += 1; // ugly hack for now
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
break;
|
||||
case MXR_CFG:
|
||||
s->mixer1_cfg = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_tvout_mixer2_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: offset = 0x%08x\n", __func__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
return s->mixer2_status;
|
||||
case MXR_CFG:
|
||||
return s->mixer2_cfg;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_mixer2_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//printf("%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
switch(offset) {
|
||||
case MXR_STATUS:
|
||||
s->mixer2_status = value;
|
||||
break;
|
||||
case MXR_CFG:
|
||||
s->mixer2_cfg = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_sdo_ops = {
|
||||
.read = ipod_touch_tvout_sdo_read,
|
||||
.write = ipod_touch_tvout_sdo_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_mixer1_ops = {
|
||||
.read = ipod_touch_tvout_mixer1_read,
|
||||
.write = ipod_touch_tvout_mixer1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_mixer2_ops = {
|
||||
.read = ipod_touch_tvout_mixer2_read,
|
||||
.write = ipod_touch_tvout_mixer2_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_tvout_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchTVOutState *s = IPOD_TOUCH_TVOUT(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
// mixer 1
|
||||
memory_region_init_io(&s->mixer1_iomem, obj, &ipod_touch_tvout_mixer1_ops, s, "tvout_mixer1", 4096);
|
||||
sysbus_init_mmio(sbd, &s->mixer1_iomem);
|
||||
|
||||
// mixer 2
|
||||
memory_region_init_io(&s->mixer2_iomem, obj, &ipod_touch_tvout_mixer2_ops, s, "tvout_mixer2", 4096);
|
||||
sysbus_init_mmio(sbd, &s->mixer2_iomem);
|
||||
|
||||
// SDO
|
||||
memory_region_init_io(&s->sdo_iomem, obj, &ipod_touch_tvout_sdo_ops, s, "tvout_sdo", 4096);
|
||||
sysbus_init_mmio(sbd, &s->sdo_iomem);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_tvout_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_TVOUT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchTVOutState),
|
||||
.instance_init = ipod_touch_tvout_init,
|
||||
.class_init = ipod_touch_tvout_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_tvout_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_tvout_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_tvout_register_types)
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "hw/arm/ipod_touch_unknown1.h"
|
||||
|
||||
static uint64_t ipod_touch_unknown1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case 0x140:
|
||||
return 0x2;
|
||||
case 0x144:
|
||||
return 0x3;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_unknown1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_unknown1_ops = {
|
||||
.read = ipod_touch_unknown1_read,
|
||||
.write = ipod_touch_unknown1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_unknown1_init(Object *obj)
|
||||
{
|
||||
IPodTouchUnknown1State *s = IPOD_TOUCH_UNKNOWN1(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_unknown1_ops, s, TYPE_IPOD_TOUCH_UNKNOWN1, 0x200);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_unknown1_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_unknown1_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_UNKNOWN1,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchUnknown1State),
|
||||
.instance_init = ipod_touch_unknown1_init,
|
||||
.class_init = ipod_touch_unknown1_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_unknown1_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_unknown1_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_unknown1_register_types)
|
||||
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Synopsys DesignWareCore for USB OTG.
|
||||
*
|
||||
* Copyright (c) 2011 Richard Ian Taylor.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/arm/ipod_touch_usb_otg.h"
|
||||
|
||||
static inline size_t synopsys_usb_tx_fifo_start(synopsys_usb_state *_state, uint32_t _fifo)
|
||||
{
|
||||
if(_fifo == 0)
|
||||
return _state->gnptxfsiz >> 16;
|
||||
else
|
||||
return _state->dptxfsiz[_fifo-1] >> 16;
|
||||
}
|
||||
|
||||
static inline size_t synopsys_usb_tx_fifo_size(synopsys_usb_state *_state, uint32_t _fifo)
|
||||
{
|
||||
if(_fifo == 0)
|
||||
return _state->gnptxfsiz & 0xFFFF;
|
||||
else
|
||||
return _state->dptxfsiz[_fifo-1] & 0xFFFF;
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_irq(synopsys_usb_state *_state)
|
||||
{
|
||||
_state->daintsts = 0;
|
||||
_state->gintsts &=~ (GINTMSK_OEP | GINTMSK_INEP | GINTMSK_OTG);
|
||||
|
||||
if(_state->gotgint)
|
||||
_state->gintsts |= GINTMSK_OTG;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < USB_NUM_ENDPOINTS; i++)
|
||||
{
|
||||
if(_state->out_eps[i].interrupt_status & _state->doepmsk)
|
||||
{
|
||||
_state->daintsts |= 1 << (i+DAINT_OUT_SHIFT);
|
||||
if(_state->daintmsk & (1 << (i+DAINT_OUT_SHIFT)))
|
||||
_state->gintsts |= GINTMSK_OEP;
|
||||
}
|
||||
|
||||
if(_state->in_eps[i].interrupt_status & _state->diepmsk)
|
||||
{
|
||||
_state->daintsts |= 1 << (i+DAINT_IN_SHIFT);
|
||||
if(_state->daintmsk & (1 << (i+DAINT_IN_SHIFT)))
|
||||
_state->gintsts |= GINTMSK_INEP;
|
||||
}
|
||||
}
|
||||
|
||||
if((_state->pcgcctl & 3) == 0 && _state->gintmsk & _state->gintsts)
|
||||
{
|
||||
//printf("USB: IRQ triggered 0x%08x & 0x%08x.\n", _state->gintsts, _state->gintmsk);
|
||||
qemu_irq_raise(_state->irq);
|
||||
}
|
||||
else
|
||||
qemu_irq_lower(_state->irq);
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_ep(synopsys_usb_state *_state, synopsys_usb_ep_state *_ep)
|
||||
{
|
||||
if(_ep->control & USB_EPCON_SETNAK)
|
||||
{
|
||||
_ep->control |= USB_EPCON_NAKSTS;
|
||||
_ep->interrupt_status |= USB_EPINT_INEPNakEff;
|
||||
_ep->control &=~ USB_EPCON_SETNAK;
|
||||
}
|
||||
|
||||
if(_ep->control & USB_EPCON_DISABLE)
|
||||
{
|
||||
_ep->interrupt_status |= USB_EPINT_EPDisbld;
|
||||
_ep->control &=~ (USB_EPCON_DISABLE | USB_EPCON_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_in_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
{
|
||||
synopsys_usb_ep_state *eps = &_state->in_eps[_ep];
|
||||
synopsys_usb_update_ep(_state, eps);
|
||||
|
||||
if(eps->control & USB_EPCON_ENABLE)
|
||||
;//printf("USB: IN transfer queued on %d.\n", _ep);
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_out_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
{
|
||||
synopsys_usb_ep_state *eps = &_state->out_eps[_ep];
|
||||
synopsys_usb_update_ep(_state, eps);
|
||||
|
||||
if(eps->control & USB_EPCON_ENABLE)
|
||||
;//printf("USB: OUT transfer queued on %d.\n", _ep);
|
||||
}
|
||||
|
||||
static uint32_t synopsys_usb_in_ep_read(synopsys_usb_state *_state, uint8_t _ep, hwaddr _addr)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Tried to read from disabled EP %d.\n", _ep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
return _state->in_eps[_ep].control;
|
||||
|
||||
case 0x08:
|
||||
return _state->in_eps[_ep].interrupt_status;
|
||||
|
||||
case 0x10:
|
||||
return _state->in_eps[_ep].tx_size;
|
||||
|
||||
case 0x14:
|
||||
return _state->in_eps[_ep].dma_address;
|
||||
|
||||
case 0x1C:
|
||||
return _state->in_eps[_ep].dma_buffer;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep read offset 0x" HWADDR_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t synopsys_usb_out_ep_read(synopsys_usb_state *_state, int _ep, hwaddr _addr)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Tried to read from disabled EP %d.\n", _ep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
return _state->out_eps[_ep].control;
|
||||
|
||||
case 0x08:
|
||||
return _state->out_eps[_ep].interrupt_status;
|
||||
|
||||
case 0x10:
|
||||
return _state->out_eps[_ep].tx_size;
|
||||
|
||||
case 0x14:
|
||||
return _state->out_eps[_ep].dma_address;
|
||||
|
||||
case 0x1C:
|
||||
return _state->out_eps[_ep].dma_buffer;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep read offset 0x" HWADDR_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t synopsys_usb_read(void *opaque, hwaddr _addr, unsigned size)
|
||||
{
|
||||
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
|
||||
|
||||
//printf("USB: Read 0x%08x.\n", _addr);
|
||||
|
||||
switch(_addr)
|
||||
{
|
||||
case PCGCCTL:
|
||||
return state->pcgcctl;
|
||||
|
||||
case GOTGCTL:
|
||||
return state->gotgctl;
|
||||
|
||||
case GOTGINT:
|
||||
return state->gotgint;
|
||||
|
||||
case GRSTCTL:
|
||||
return state->grstctl;
|
||||
|
||||
case GHWCFG1:
|
||||
return state->ghwcfg1;
|
||||
|
||||
case GHWCFG2:
|
||||
return state->ghwcfg2;
|
||||
|
||||
case GHWCFG3:
|
||||
return state->ghwcfg3;
|
||||
|
||||
case GHWCFG4:
|
||||
return state->ghwcfg4;
|
||||
|
||||
case GAHBCFG:
|
||||
return state->gahbcfg;
|
||||
|
||||
case GUSBCFG:
|
||||
return state->gusbcfg;
|
||||
|
||||
case GINTMSK:
|
||||
return state->gintmsk;
|
||||
|
||||
case GINTSTS:
|
||||
return state->gintsts;
|
||||
|
||||
case DIEPMSK:
|
||||
return state->diepmsk;
|
||||
|
||||
case DOEPMSK:
|
||||
return state->doepmsk;
|
||||
|
||||
case DAINTMSK:
|
||||
return state->daintmsk;
|
||||
|
||||
case DAINTSTS:
|
||||
return state->daintsts;
|
||||
|
||||
case DCTL:
|
||||
return state->dctl;
|
||||
|
||||
case DCFG:
|
||||
return state->dcfg;
|
||||
|
||||
case DSTS:
|
||||
return state->dsts;
|
||||
|
||||
case GRXSTSR:
|
||||
case GRXSTSP:
|
||||
return 0; // TODO: Do something about this?
|
||||
|
||||
case GNPTXFSTS:
|
||||
return 0xFFFFFFFF;
|
||||
|
||||
case GRXFSIZ:
|
||||
return state->grxfsiz;
|
||||
|
||||
case GNPTXFSIZ:
|
||||
return state->gnptxfsiz;
|
||||
|
||||
case DIEPTXF(1) ... DIEPTXF(USB_NUM_FIFOS+1):
|
||||
_addr -= DIEPTXF(1);
|
||||
_addr >>= 2;
|
||||
return state->dptxfsiz[_addr];
|
||||
|
||||
case USB_INREGS ... (USB_INREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_INREGS;
|
||||
return synopsys_usb_in_ep_read(state, _addr >> 5, _addr & 0x1f);
|
||||
|
||||
case USB_OUTREGS ... (USB_OUTREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_OUTREGS;
|
||||
return synopsys_usb_out_ep_read(state, _addr >> 5, _addr & 0x1f);
|
||||
|
||||
case USB_FIFO_START ... USB_FIFO_END-4:
|
||||
_addr -= USB_FIFO_START;
|
||||
return *((uint32_t*)(&state->fifos[_addr]));
|
||||
|
||||
default:
|
||||
hw_error("USB: Unhandled read address 0x%08x!\n", _addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void synopsys_usb_in_ep_write(synopsys_usb_state *_state, int _ep, hwaddr _addr, uint32_t _val)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Wrote to disabled EP %d.\n", _ep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
_state->in_eps[_ep].control = _val;
|
||||
synopsys_usb_update_in_ep(_state, _ep);
|
||||
return;
|
||||
|
||||
case 0x08:
|
||||
_state->in_eps[_ep].interrupt_status &=~ _val;
|
||||
synopsys_usb_update_irq(_state);
|
||||
return;
|
||||
|
||||
case 0x10:
|
||||
_state->in_eps[_ep].tx_size = _val;
|
||||
return;
|
||||
|
||||
case 0x14:
|
||||
_state->in_eps[_ep].dma_address = _val;
|
||||
return;
|
||||
|
||||
case 0x1C:
|
||||
_state->in_eps[_ep].dma_buffer = _val;
|
||||
return;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep write offset 0x" HWADDR_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void synopsys_usb_out_ep_write(synopsys_usb_state *_state, int _ep, hwaddr _addr, uint32_t _val)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Wrote to disabled EP %d.\n", _ep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
_state->out_eps[_ep].control = _val;
|
||||
synopsys_usb_update_out_ep(_state, _ep);
|
||||
return;
|
||||
|
||||
case 0x08:
|
||||
_state->out_eps[_ep].interrupt_status &=~ _val;
|
||||
synopsys_usb_update_irq(_state);
|
||||
return;
|
||||
|
||||
case 0x10:
|
||||
_state->out_eps[_ep].tx_size = _val;
|
||||
return;
|
||||
|
||||
case 0x14:
|
||||
_state->out_eps[_ep].dma_address = _val;
|
||||
return;
|
||||
|
||||
case 0x1C:
|
||||
_state->out_eps[_ep].dma_buffer = _val;
|
||||
return;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep write offset 0x" HWADDR_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void synopsys_usb_write(void *opaque, hwaddr _addr, uint64_t _val, unsigned size)
|
||||
{
|
||||
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
|
||||
|
||||
//printf("USB: Write 0x%08x to 0x%08x.\n", _val, _addr);
|
||||
|
||||
switch(_addr)
|
||||
{
|
||||
case PCGCCTL:
|
||||
state->pcgcctl = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case GOTGCTL:
|
||||
state->gotgctl = _val;
|
||||
break;
|
||||
|
||||
case GOTGINT:
|
||||
state->gotgint &=~ _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case GRSTCTL:
|
||||
if(_val & GRSTCTL_CORESOFTRESET)
|
||||
{
|
||||
state->grstctl = GRSTCTL_CORESOFTRESET;
|
||||
|
||||
// Do reset stuff
|
||||
// if(state->server_host)
|
||||
// {
|
||||
// tcp_usb_cleanup(&state->tcp_state);
|
||||
// tcp_usb_init(&state->tcp_state, synopsys_usb_tcp_callback, NULL, state);
|
||||
|
||||
// printf("Connecting to USB server at %s:%d...\n",
|
||||
// state->server_host, state->server_port);
|
||||
|
||||
// int ret = tcp_usb_connect(&state->tcp_state, state->server_host, state->server_port);
|
||||
// if(ret < 0)
|
||||
// hw_error("Failed to connect to USB server (%d).\n", ret);
|
||||
|
||||
// printf("Connected to USB server.\n");
|
||||
// }
|
||||
|
||||
state->grstctl &= ~GRSTCTL_CORESOFTRESET;
|
||||
state->grstctl |= GRSTCTL_AHBIDLE;
|
||||
state->gintsts |= GINTMSK_RESET;
|
||||
synopsys_usb_update_irq(state);
|
||||
}
|
||||
else if(_val == 0)
|
||||
state->grstctl = _val;
|
||||
|
||||
return;
|
||||
|
||||
case GINTMSK:
|
||||
state->gintmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
break;
|
||||
|
||||
case GINTSTS:
|
||||
state->gintsts &=~ _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DOEPMSK:
|
||||
state->doepmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DIEPMSK:
|
||||
state->diepmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DAINTMSK:
|
||||
state->daintmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DAINTSTS:
|
||||
state->daintsts &=~ _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case GAHBCFG:
|
||||
state->gahbcfg = _val;
|
||||
return;
|
||||
|
||||
case GUSBCFG:
|
||||
state->gusbcfg = _val;
|
||||
return;
|
||||
|
||||
case DCTL:
|
||||
if((_val & DCTL_SGNPINNAK) != (state->dctl & DCTL_SGNPINNAK)
|
||||
&& (_val & DCTL_SGNPINNAK))
|
||||
{
|
||||
state->gintsts |= GINTMSK_GINNAKEFF;
|
||||
_val &=~ DCTL_SGNPINNAK;
|
||||
}
|
||||
|
||||
if((_val & DCTL_SGOUTNAK) != (state->dctl & DCTL_SGOUTNAK)
|
||||
&& (_val & DCTL_SGOUTNAK))
|
||||
{
|
||||
state->gintsts |= GINTMSK_GOUTNAKEFF;
|
||||
_val &=~ DCTL_SGOUTNAK;
|
||||
}
|
||||
|
||||
state->dctl = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DCFG:
|
||||
//printf("USB: dcfg = 0x%08x.\n", _val);
|
||||
state->dcfg = _val;
|
||||
return;
|
||||
|
||||
case GRXFSIZ:
|
||||
state->grxfsiz = _val;
|
||||
return;
|
||||
|
||||
case GNPTXFSIZ:
|
||||
state->gnptxfsiz = _val;
|
||||
return;
|
||||
|
||||
case DIEPTXF(1) ... DIEPTXF(USB_NUM_FIFOS+1):
|
||||
_addr -= DIEPTXF(1);
|
||||
_addr >>= 2;
|
||||
state->dptxfsiz[_addr] = _val;
|
||||
return;
|
||||
|
||||
case USB_INREGS ... (USB_INREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_INREGS;
|
||||
synopsys_usb_in_ep_write(state, _addr >> 5, _addr & 0x1f, _val);
|
||||
return;
|
||||
|
||||
case USB_OUTREGS ... (USB_OUTREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_OUTREGS;
|
||||
synopsys_usb_out_ep_write(state, _addr >> 5, _addr & 0x1f, _val);
|
||||
return;
|
||||
|
||||
case USB_FIFO_START ... USB_FIFO_END-4:
|
||||
_addr -= USB_FIFO_START;
|
||||
*((uint32_t*)(&state->fifos[_addr])) = _val;
|
||||
return;
|
||||
|
||||
default:
|
||||
hw_error("USB: Unhandled write address 0x%08x!\n", _addr);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps usb_otg_ops = {
|
||||
.read = synopsys_usb_read,
|
||||
.write = synopsys_usb_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_usb_otg_reset(DeviceState *d)
|
||||
{
|
||||
synopsys_usb_state *state = S5L8900USBOTG(d);
|
||||
|
||||
printf("USB: cfg = 0x%08x, 0x%08x, 0x%08x, 0x%08x.\n",
|
||||
state->ghwcfg1,
|
||||
state->ghwcfg2,
|
||||
state->ghwcfg3,
|
||||
state->ghwcfg4);
|
||||
|
||||
state->pcgcctl = 3;
|
||||
|
||||
state->gahbcfg = 0;
|
||||
state->gusbcfg = 0;
|
||||
|
||||
state->dctl = 0;
|
||||
state->dcfg = 0;
|
||||
state->dsts = 0;
|
||||
|
||||
state->gotgctl = 0;
|
||||
state->gotgint = 0;
|
||||
|
||||
state->gintmsk = 0;
|
||||
state->gintsts = 0;
|
||||
|
||||
state->daintmsk = 0;
|
||||
state->daintsts = 0;
|
||||
|
||||
state->diepmsk = 0;
|
||||
state->doepmsk = 0;
|
||||
|
||||
state->grxfsiz = 0x100;
|
||||
state->gnptxfsiz = (0x100 << 16) | 0x100;
|
||||
|
||||
uint32_t counter = 0x200;
|
||||
int i;
|
||||
for(i = 0; i < USB_NUM_FIFOS; i++)
|
||||
{
|
||||
state->dptxfsiz[i] = (counter << 16) | 0x100;
|
||||
counter += 0x100;
|
||||
}
|
||||
|
||||
for(i = 0; i < USB_NUM_ENDPOINTS; i++)
|
||||
{
|
||||
synopsys_usb_ep_state *in = &state->in_eps[i];
|
||||
in->control = 0;
|
||||
in->dma_address = 0;
|
||||
in->fifo = 0;
|
||||
in->tx_size = 0;
|
||||
|
||||
synopsys_usb_ep_state *out = &state->out_eps[i];
|
||||
out->control = 0;
|
||||
out->dma_address = 0;
|
||||
out->fifo = 0;
|
||||
out->tx_size = 0;
|
||||
}
|
||||
|
||||
synopsys_usb_update_irq(state);
|
||||
}
|
||||
|
||||
static void s5l8900_usb_otg_init1(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
synopsys_usb_state *s = S5L8900USBOTG(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &usb_otg_ops, s, "usb_otg", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
// Helper for adding to a machine
|
||||
DeviceState *ipod_touch_init_usb_otg(qemu_irq _irq, uint32_t _hwcfg[4])
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_S5L8900USBOTG);
|
||||
synopsys_usb_state *state = S5L8900USBOTG(dev);
|
||||
|
||||
state->ghwcfg1 = _hwcfg[0];
|
||||
state->ghwcfg2 = _hwcfg[1];
|
||||
state->ghwcfg3 = _hwcfg[2];
|
||||
state->ghwcfg4 = _hwcfg[3];
|
||||
|
||||
SysBusDevice *sdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(sdev, 0, _irq);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void s5l8900_usb_otg_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->reset = s5l8900_usb_otg_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo s5l8900_usb_otg_info = {
|
||||
.name = TYPE_S5L8900USBOTG,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(synopsys_usb_state),
|
||||
.instance_init = s5l8900_usb_otg_init1,
|
||||
.class_init = s5l8900_usb_otg_class_init,
|
||||
};
|
||||
|
||||
static void s5l8900_usb_otg_register_types(void)
|
||||
{
|
||||
type_register_static(&s5l8900_usb_otg_info);
|
||||
}
|
||||
|
||||
type_init(s5l8900_usb_otg_register_types)
|
||||
@@ -0,0 +1,86 @@
|
||||
#include "hw/arm/ipod_touch_usb_phys.h"
|
||||
|
||||
static uint64_t ipod_touch_usb_phys_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchUSBPhysState *s = (IPodTouchUSBPhysState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case REG_OPHYPWR:
|
||||
return s->usb_ophypwr;
|
||||
case REG_OPHYCLK:
|
||||
return s->usb_ophyclk;
|
||||
case REG_ORSTCON:
|
||||
return s->usb_orstcon;
|
||||
case REG_UNKNOWN1:
|
||||
return s->usb_unknown1;
|
||||
case REG_OPHYTUNE:
|
||||
return s->usb_ophytune;
|
||||
default:
|
||||
return 0x0;
|
||||
//hw_error("%s: read invalid location 0x%08x\n", __func__, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_usb_phys_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchUSBPhysState *s = (IPodTouchUSBPhysState *) opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case REG_OPHYPWR:
|
||||
s->usb_ophypwr = val;
|
||||
return;
|
||||
case REG_OPHYCLK:
|
||||
s->usb_ophyclk = val;
|
||||
return;
|
||||
case REG_ORSTCON:
|
||||
s->usb_orstcon = val;
|
||||
return;
|
||||
case REG_UNKNOWN1:
|
||||
s->usb_unknown1 = val;
|
||||
return;
|
||||
case REG_OPHYTUNE:
|
||||
s->usb_ophytune = val;
|
||||
return;
|
||||
|
||||
default:
|
||||
//hw_error("%s: write invalid location 0x%08x.\n", __func__, addr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_usb_phys_ops = {
|
||||
.read = ipod_touch_usb_phys_read,
|
||||
.write = ipod_touch_usb_phys_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_usb_phys_init(Object *obj)
|
||||
{
|
||||
IPodTouchUSBPhysState *s = IPOD_TOUCH_USB_PHYS(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_usb_phys_ops, s, TYPE_IPOD_TOUCH_USB_PHYS, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_usb_phys_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_usb_phys_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_USB_PHYS,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchUSBPhysState),
|
||||
.instance_init = ipod_touch_usb_phys_init,
|
||||
.class_init = ipod_touch_usb_phys_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_usb_phys_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_usb_phys_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_usb_phys_register_types)
|
||||
+1
-1
@@ -65,7 +65,7 @@ arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
|
||||
arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
|
||||
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
|
||||
arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
|
||||
arm_ss.add(when: 'CONFIG_ATV4', if_true: files('atv4.c', 'atv4_chipid.c', 'atv4_pmgr.c', 'atv4_gpio.c', 'atv4_ausb.c', 'atv4_ausb_widget.c', 'atv4_spi.c', 'atv4_anc.c'))
|
||||
arm_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ipod_touch_2g.c', 'ipod_touch_clock.c', 'ipod_touch_chipid.c', 'ipod_touch_gpio.c', 'ipod_touch_sysic.c', 'ipod_touch_timer.c', 'ipod_touch_usb_otg.c', 'ipod_touch_usb_phys.c', 'ipod_touch_spi.c', 'ipod_touch_nor_spi.c', 'ipod_touch_sha1.c', 'ipod_touch_aes.c', 'ipod_touch_pke.c', 'ipod_touch_pcf50633_pmu.c', 'ipod_touch_unknown1.c', 'ipod_touch_lcd.c', 'ipod_touch_mipi_dsi.c', 'ipod_touch_fmss.c', 'ipod_touch_mbx.c', 'ipod_touch_cd3272_mikey.c', 'ipod_touch_lis302dl.c', 'ipod_touch_multitouch.c', 'ipod_touch_scaler_csc.c', 'ipod_touch_sdio.c', 'ipod_touch_tvout.c'))
|
||||
|
||||
system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
|
||||
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c'))
|
||||
|
||||
+1
-2
@@ -24,7 +24,6 @@
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
@@ -378,7 +377,7 @@ static void npcm7xx_init_fuses(NPCM7xxState *s)
|
||||
* The initial mask of disabled modules indicates the chip derivative (e.g.
|
||||
* NPCM750 or NPCM730).
|
||||
*/
|
||||
value = cpu_to_le32(nc->disabled_modules);
|
||||
value = tswap32(nc->disabled_modules);
|
||||
npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
|
||||
sizeof(value));
|
||||
}
|
||||
|
||||
@@ -675,8 +675,6 @@ static void smmu_base_reset_hold(Object *obj)
|
||||
{
|
||||
SMMUState *s = ARM_SMMU(obj);
|
||||
|
||||
memset(s->smmu_pcibus_by_bus_num, 0, sizeof(s->smmu_pcibus_by_bus_num));
|
||||
|
||||
g_hash_table_remove_all(s->configs);
|
||||
g_hash_table_remove_all(s->iotlb);
|
||||
}
|
||||
|
||||
+71
-65
@@ -243,13 +243,12 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
|
||||
memset(&pcm_info[i].padding, 0, 5);
|
||||
}
|
||||
|
||||
cmd->payload_size = sizeof(virtio_snd_pcm_info) * count;
|
||||
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
|
||||
iov_from_buf(cmd->elem->in_sg,
|
||||
cmd->elem->in_num,
|
||||
sizeof(virtio_snd_hdr),
|
||||
pcm_info,
|
||||
cmd->payload_size);
|
||||
sizeof(virtio_snd_pcm_info) * count);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -456,6 +455,7 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
|
||||
stream->s = s;
|
||||
qemu_mutex_init(&stream->queue_mutex);
|
||||
QSIMPLEQ_INIT(&stream->queue);
|
||||
QSIMPLEQ_INIT(&stream->invalid);
|
||||
|
||||
/*
|
||||
* stream_id >= s->snd_conf.streams was checked before so this is
|
||||
@@ -610,6 +610,9 @@ static size_t virtio_snd_pcm_get_io_msgs_count(VirtIOSoundPCMStream *stream)
|
||||
QSIMPLEQ_FOREACH_SAFE(buffer, &stream->queue, entry, next) {
|
||||
count += 1;
|
||||
}
|
||||
QSIMPLEQ_FOREACH_SAFE(buffer, &stream->invalid, entry, next) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -746,8 +749,7 @@ process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
|
||||
0,
|
||||
&cmd->resp,
|
||||
sizeof(virtio_snd_hdr));
|
||||
virtqueue_push(cmd->vq, cmd->elem,
|
||||
sizeof(virtio_snd_hdr) + cmd->payload_size);
|
||||
virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr));
|
||||
virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
|
||||
}
|
||||
|
||||
@@ -806,7 +808,6 @@ static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
|
||||
cmd->elem = elem;
|
||||
cmd->vq = vq;
|
||||
cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
|
||||
/* implicit cmd->payload_size = 0; */
|
||||
QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next);
|
||||
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
||||
}
|
||||
@@ -827,36 +828,47 @@ static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
|
||||
trace_virtio_snd_handle_event();
|
||||
}
|
||||
|
||||
/*
|
||||
* Must only be called if vsnd->invalid is not empty.
|
||||
*/
|
||||
static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
VirtIOSoundPCMBuffer *buffer = NULL;
|
||||
VirtIOSoundPCMStream *stream = NULL;
|
||||
virtio_snd_pcm_status resp = { 0 };
|
||||
VirtIOSound *vsnd = VIRTIO_SND(vdev);
|
||||
bool any = false;
|
||||
|
||||
g_assert(!QSIMPLEQ_EMPTY(&vsnd->invalid));
|
||||
|
||||
while (!QSIMPLEQ_EMPTY(&vsnd->invalid)) {
|
||||
buffer = QSIMPLEQ_FIRST(&vsnd->invalid);
|
||||
/* If buffer->vq != vq, our logic is fundamentally wrong, so bail out */
|
||||
g_assert(buffer->vq == vq);
|
||||
|
||||
resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
|
||||
iov_from_buf(buffer->elem->in_sg,
|
||||
buffer->elem->in_num,
|
||||
0,
|
||||
&resp,
|
||||
sizeof(virtio_snd_pcm_status));
|
||||
virtqueue_push(vq,
|
||||
buffer->elem,
|
||||
sizeof(virtio_snd_pcm_status));
|
||||
QSIMPLEQ_REMOVE_HEAD(&vsnd->invalid, entry);
|
||||
virtio_snd_pcm_buffer_free(buffer);
|
||||
for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
|
||||
stream = vsnd->pcm->streams[i];
|
||||
if (stream) {
|
||||
any = false;
|
||||
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
|
||||
while (!QSIMPLEQ_EMPTY(&stream->invalid)) {
|
||||
buffer = QSIMPLEQ_FIRST(&stream->invalid);
|
||||
if (buffer->vq != vq) {
|
||||
break;
|
||||
}
|
||||
any = true;
|
||||
resp.status = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
|
||||
iov_from_buf(buffer->elem->in_sg,
|
||||
buffer->elem->in_num,
|
||||
0,
|
||||
&resp,
|
||||
sizeof(virtio_snd_pcm_status));
|
||||
virtqueue_push(vq,
|
||||
buffer->elem,
|
||||
sizeof(virtio_snd_pcm_status));
|
||||
QSIMPLEQ_REMOVE_HEAD(&stream->invalid, entry);
|
||||
virtio_snd_pcm_buffer_free(buffer);
|
||||
}
|
||||
if (any) {
|
||||
/*
|
||||
* Notify vq about virtio_snd_pcm_status responses.
|
||||
* Buffer responses must be notified separately later.
|
||||
*/
|
||||
virtio_notify(vdev, vq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Notify vq about virtio_snd_pcm_status responses. */
|
||||
virtio_notify(vdev, vq);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -868,14 +880,15 @@ static inline void empty_invalid_queue(VirtIODevice *vdev, VirtQueue *vq)
|
||||
*/
|
||||
static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
VirtIOSound *vsnd = VIRTIO_SND(vdev);
|
||||
VirtIOSound *s = VIRTIO_SND(vdev);
|
||||
VirtIOSoundPCMStream *stream = NULL;
|
||||
VirtIOSoundPCMBuffer *buffer;
|
||||
VirtQueueElement *elem;
|
||||
size_t msg_sz, size;
|
||||
virtio_snd_pcm_xfer hdr;
|
||||
uint32_t stream_id;
|
||||
/*
|
||||
* If any of the I/O messages are invalid, put them in vsnd->invalid and
|
||||
* If any of the I/O messages are invalid, put them in stream->invalid and
|
||||
* return them after the for loop.
|
||||
*/
|
||||
bool must_empty_invalid_queue = false;
|
||||
@@ -886,8 +899,6 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
trace_virtio_snd_handle_tx_xfer();
|
||||
|
||||
for (;;) {
|
||||
VirtIOSoundPCMStream *stream;
|
||||
|
||||
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
||||
if (!elem) {
|
||||
break;
|
||||
@@ -903,12 +914,12 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
}
|
||||
stream_id = le32_to_cpu(hdr.stream_id);
|
||||
|
||||
if (stream_id >= vsnd->snd_conf.streams
|
||||
|| vsnd->pcm->streams[stream_id] == NULL) {
|
||||
if (stream_id >= s->snd_conf.streams
|
||||
|| s->pcm->streams[stream_id] == NULL) {
|
||||
goto tx_err;
|
||||
}
|
||||
|
||||
stream = vsnd->pcm->streams[stream_id];
|
||||
stream = s->pcm->streams[stream_id];
|
||||
if (stream->info.direction != VIRTIO_SND_D_OUTPUT) {
|
||||
goto tx_err;
|
||||
}
|
||||
@@ -928,11 +939,13 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
continue;
|
||||
|
||||
tx_err:
|
||||
must_empty_invalid_queue = true;
|
||||
buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
|
||||
buffer->elem = elem;
|
||||
buffer->vq = vq;
|
||||
QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry);
|
||||
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
|
||||
must_empty_invalid_queue = true;
|
||||
buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
|
||||
buffer->elem = elem;
|
||||
buffer->vq = vq;
|
||||
QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (must_empty_invalid_queue) {
|
||||
@@ -949,14 +962,15 @@ tx_err:
|
||||
*/
|
||||
static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
{
|
||||
VirtIOSound *vsnd = VIRTIO_SND(vdev);
|
||||
VirtIOSound *s = VIRTIO_SND(vdev);
|
||||
VirtIOSoundPCMStream *stream = NULL;
|
||||
VirtIOSoundPCMBuffer *buffer;
|
||||
VirtQueueElement *elem;
|
||||
size_t msg_sz, size;
|
||||
virtio_snd_pcm_xfer hdr;
|
||||
uint32_t stream_id;
|
||||
/*
|
||||
* if any of the I/O messages are invalid, put them in vsnd->invalid and
|
||||
* if any of the I/O messages are invalid, put them in stream->invalid and
|
||||
* return them after the for loop.
|
||||
*/
|
||||
bool must_empty_invalid_queue = false;
|
||||
@@ -967,8 +981,6 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
trace_virtio_snd_handle_rx_xfer();
|
||||
|
||||
for (;;) {
|
||||
VirtIOSoundPCMStream *stream;
|
||||
|
||||
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
|
||||
if (!elem) {
|
||||
break;
|
||||
@@ -984,12 +996,12 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
}
|
||||
stream_id = le32_to_cpu(hdr.stream_id);
|
||||
|
||||
if (stream_id >= vsnd->snd_conf.streams
|
||||
|| !vsnd->pcm->streams[stream_id]) {
|
||||
if (stream_id >= s->snd_conf.streams
|
||||
|| !s->pcm->streams[stream_id]) {
|
||||
goto rx_err;
|
||||
}
|
||||
|
||||
stream = vsnd->pcm->streams[stream_id];
|
||||
stream = s->pcm->streams[stream_id];
|
||||
if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) {
|
||||
goto rx_err;
|
||||
}
|
||||
@@ -1006,11 +1018,13 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq)
|
||||
continue;
|
||||
|
||||
rx_err:
|
||||
must_empty_invalid_queue = true;
|
||||
buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
|
||||
buffer->elem = elem;
|
||||
buffer->vq = vq;
|
||||
QSIMPLEQ_INSERT_TAIL(&vsnd->invalid, buffer, entry);
|
||||
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
|
||||
must_empty_invalid_queue = true;
|
||||
buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer));
|
||||
buffer->elem = elem;
|
||||
buffer->vq = vq;
|
||||
QSIMPLEQ_INSERT_TAIL(&stream->invalid, buffer, entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (must_empty_invalid_queue) {
|
||||
@@ -1110,7 +1124,6 @@ static void virtio_snd_realize(DeviceState *dev, Error **errp)
|
||||
virtio_add_queue(vdev, 64, virtio_snd_handle_rx_xfer);
|
||||
qemu_mutex_init(&vsnd->cmdq_mutex);
|
||||
QTAILQ_INIT(&vsnd->cmdq);
|
||||
QSIMPLEQ_INIT(&vsnd->invalid);
|
||||
|
||||
for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
|
||||
status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
|
||||
@@ -1360,20 +1373,13 @@ static void virtio_snd_unrealize(DeviceState *dev)
|
||||
|
||||
static void virtio_snd_reset(VirtIODevice *vdev)
|
||||
{
|
||||
VirtIOSound *vsnd = VIRTIO_SND(vdev);
|
||||
VirtIOSound *s = VIRTIO_SND(vdev);
|
||||
virtio_snd_ctrl_command *cmd;
|
||||
|
||||
/*
|
||||
* Sanity check that the invalid buffer message queue is emptied at the end
|
||||
* of every virtio_snd_handle_tx_xfer/virtio_snd_handle_rx_xfer call, and
|
||||
* must be empty otherwise.
|
||||
*/
|
||||
g_assert(QSIMPLEQ_EMPTY(&vsnd->invalid));
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&vsnd->cmdq_mutex) {
|
||||
while (!QTAILQ_EMPTY(&vsnd->cmdq)) {
|
||||
cmd = QTAILQ_FIRST(&vsnd->cmdq);
|
||||
QTAILQ_REMOVE(&vsnd->cmdq, cmd, next);
|
||||
WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
|
||||
while (!QTAILQ_EMPTY(&s->cmdq)) {
|
||||
cmd = QTAILQ_FIRST(&s->cmdq);
|
||||
QTAILQ_REMOVE(&s->cmdq, cmd, next);
|
||||
virtio_snd_ctrl_cmd_free(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-38
@@ -84,11 +84,7 @@ struct NANDFlashState {
|
||||
|
||||
void (*blk_write)(NANDFlashState *s);
|
||||
void (*blk_erase)(NANDFlashState *s);
|
||||
/*
|
||||
* Returns %true when block containing (@addr + @offset) is
|
||||
* successfully loaded, otherwise %false.
|
||||
*/
|
||||
bool (*blk_load)(NANDFlashState *s, uint64_t addr, unsigned offset);
|
||||
void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset);
|
||||
|
||||
uint32_t ioaddr_vmstate;
|
||||
};
|
||||
@@ -247,30 +243,9 @@ static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* nand_load_block: Load block containing (s->addr + @offset).
|
||||
* Returns length of data available at @offset in this block.
|
||||
*/
|
||||
static unsigned nand_load_block(NANDFlashState *s, unsigned offset)
|
||||
{
|
||||
unsigned iolen;
|
||||
|
||||
if (!s->blk_load(s, s->addr, offset)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
iolen = (1 << s->page_shift);
|
||||
if (s->gnd) {
|
||||
iolen += 1 << s->oob_shift;
|
||||
}
|
||||
assert(offset <= iolen);
|
||||
iolen -= offset;
|
||||
|
||||
return iolen;
|
||||
}
|
||||
|
||||
static void nand_command(NANDFlashState *s)
|
||||
{
|
||||
unsigned int offset;
|
||||
switch (s->cmd) {
|
||||
case NAND_CMD_READ0:
|
||||
s->iolen = 0;
|
||||
@@ -296,7 +271,12 @@ static void nand_command(NANDFlashState *s)
|
||||
case NAND_CMD_NOSERIALREAD2:
|
||||
if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
|
||||
break;
|
||||
s->iolen = nand_load_block(s, s->addr & ((1 << s->addr_shift) - 1));
|
||||
offset = s->addr & ((1 << s->addr_shift) - 1);
|
||||
s->blk_load(s, s->addr, offset);
|
||||
if (s->gnd)
|
||||
s->iolen = (1 << s->page_shift) - offset;
|
||||
else
|
||||
s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
|
||||
break;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
@@ -617,7 +597,12 @@ uint32_t nand_getio(DeviceState *dev)
|
||||
if (!s->iolen && s->cmd == NAND_CMD_READ0) {
|
||||
offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
|
||||
s->offset = 0;
|
||||
s->iolen = nand_load_block(s, offset);
|
||||
|
||||
s->blk_load(s, s->addr, offset);
|
||||
if (s->gnd)
|
||||
s->iolen = (1 << s->page_shift) - offset;
|
||||
else
|
||||
s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
|
||||
}
|
||||
|
||||
if (s->ce || s->iolen <= 0) {
|
||||
@@ -778,15 +763,11 @@ static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static bool glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s,
|
||||
uint64_t addr, unsigned offset)
|
||||
static void glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s,
|
||||
uint64_t addr, int offset)
|
||||
{
|
||||
if (PAGE(addr) >= s->pages) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset > NAND_PAGE_SIZE + OOB_SIZE) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->blk) {
|
||||
@@ -814,8 +795,6 @@ static bool glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s,
|
||||
offset, NAND_PAGE_SIZE + OOB_SIZE - offset);
|
||||
s->ioaddr = s->io;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void glue(nand_init_, NAND_PAGE_SIZE)(NANDFlashState *s)
|
||||
|
||||
+80
-91
@@ -80,39 +80,16 @@ struct PFlashCFI01 {
|
||||
uint16_t ident3;
|
||||
uint8_t cfi_table[0x52];
|
||||
uint64_t counter;
|
||||
uint32_t writeblock_size;
|
||||
unsigned int writeblock_size;
|
||||
MemoryRegion mem;
|
||||
char *name;
|
||||
void *storage;
|
||||
VMChangeStateEntry *vmstate;
|
||||
bool old_multiple_chip_handling;
|
||||
|
||||
/* block update buffer */
|
||||
unsigned char *blk_bytes;
|
||||
uint32_t blk_offset;
|
||||
};
|
||||
|
||||
static int pflash_post_load(void *opaque, int version_id);
|
||||
|
||||
static bool pflash_blk_write_state_needed(void *opaque)
|
||||
{
|
||||
PFlashCFI01 *pfl = opaque;
|
||||
|
||||
return (pfl->blk_offset != -1);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pflash_blk_write = {
|
||||
.name = "pflash_cfi01_blk_write",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = pflash_blk_write_state_needed,
|
||||
.fields = (const VMStateField[]) {
|
||||
VMSTATE_VBUFFER_UINT32(blk_bytes, PFlashCFI01, 0, NULL, writeblock_size),
|
||||
VMSTATE_UINT32(blk_offset, PFlashCFI01),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_pflash = {
|
||||
.name = "pflash_cfi01",
|
||||
.version_id = 1,
|
||||
@@ -124,10 +101,6 @@ static const VMStateDescription vmstate_pflash = {
|
||||
VMSTATE_UINT8(status, PFlashCFI01),
|
||||
VMSTATE_UINT64(counter, PFlashCFI01),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&vmstate_pflash_blk_write,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -252,10 +225,34 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset,
|
||||
uint32_t ret;
|
||||
|
||||
p = pfl->storage;
|
||||
if (be) {
|
||||
ret = ldn_be_p(p + offset, width);
|
||||
} else {
|
||||
ret = ldn_le_p(p + offset, width);
|
||||
switch (width) {
|
||||
case 1:
|
||||
ret = p[offset];
|
||||
break;
|
||||
case 2:
|
||||
if (be) {
|
||||
ret = p[offset] << 8;
|
||||
ret |= p[offset + 1];
|
||||
} else {
|
||||
ret = p[offset];
|
||||
ret |= p[offset + 1] << 8;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (be) {
|
||||
ret = p[offset] << 24;
|
||||
ret |= p[offset + 1] << 16;
|
||||
ret |= p[offset + 2] << 8;
|
||||
ret |= p[offset + 3];
|
||||
} else {
|
||||
ret = p[offset];
|
||||
ret |= p[offset + 1] << 8;
|
||||
ret |= p[offset + 2] << 16;
|
||||
ret |= p[offset + 3] << 24;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
trace_pflash_data_read(pfl->name, offset, width, ret);
|
||||
return ret;
|
||||
@@ -403,61 +400,40 @@ static void pflash_update(PFlashCFI01 *pfl, int offset,
|
||||
}
|
||||
}
|
||||
|
||||
/* copy current flash content to block update buffer */
|
||||
static void pflash_blk_write_start(PFlashCFI01 *pfl, hwaddr offset)
|
||||
{
|
||||
hwaddr mask = ~(pfl->writeblock_size - 1);
|
||||
|
||||
trace_pflash_write_block_start(pfl->name, pfl->counter);
|
||||
pfl->blk_offset = offset & mask;
|
||||
memcpy(pfl->blk_bytes, pfl->storage + pfl->blk_offset,
|
||||
pfl->writeblock_size);
|
||||
}
|
||||
|
||||
/* commit block update buffer changes */
|
||||
static void pflash_blk_write_flush(PFlashCFI01 *pfl)
|
||||
{
|
||||
g_assert(pfl->blk_offset != -1);
|
||||
trace_pflash_write_block_flush(pfl->name);
|
||||
memcpy(pfl->storage + pfl->blk_offset, pfl->blk_bytes,
|
||||
pfl->writeblock_size);
|
||||
pflash_update(pfl, pfl->blk_offset, pfl->writeblock_size);
|
||||
pfl->blk_offset = -1;
|
||||
}
|
||||
|
||||
/* discard block update buffer changes */
|
||||
static void pflash_blk_write_abort(PFlashCFI01 *pfl)
|
||||
{
|
||||
trace_pflash_write_block_abort(pfl->name);
|
||||
pfl->blk_offset = -1;
|
||||
}
|
||||
|
||||
static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
uint32_t value, int width, int be)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *p = pfl->storage;
|
||||
|
||||
if (pfl->blk_offset != -1) {
|
||||
/* block write: redirect writes to block update buffer */
|
||||
if ((offset < pfl->blk_offset) ||
|
||||
(offset + width > pfl->blk_offset + pfl->writeblock_size)) {
|
||||
pfl->status |= 0x10; /* Programming error */
|
||||
return;
|
||||
trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter);
|
||||
switch (width) {
|
||||
case 1:
|
||||
p[offset] = value;
|
||||
break;
|
||||
case 2:
|
||||
if (be) {
|
||||
p[offset] = value >> 8;
|
||||
p[offset + 1] = value;
|
||||
} else {
|
||||
p[offset] = value;
|
||||
p[offset + 1] = value >> 8;
|
||||
}
|
||||
trace_pflash_data_write_block(pfl->name, offset, width, value,
|
||||
pfl->counter);
|
||||
p = pfl->blk_bytes + (offset - pfl->blk_offset);
|
||||
} else {
|
||||
/* write directly to storage */
|
||||
trace_pflash_data_write(pfl->name, offset, width, value);
|
||||
p = pfl->storage + offset;
|
||||
break;
|
||||
case 4:
|
||||
if (be) {
|
||||
p[offset] = value >> 24;
|
||||
p[offset + 1] = value >> 16;
|
||||
p[offset + 2] = value >> 8;
|
||||
p[offset + 3] = value;
|
||||
} else {
|
||||
p[offset] = value;
|
||||
p[offset + 1] = value >> 8;
|
||||
p[offset + 2] = value >> 16;
|
||||
p[offset + 3] = value >> 24;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (be) {
|
||||
stn_be_p(p, width, value);
|
||||
} else {
|
||||
stn_le_p(p, width, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
@@ -572,9 +548,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
} else {
|
||||
value = extract32(value, 0, pfl->bank_width * 8);
|
||||
}
|
||||
trace_pflash_write_block(pfl->name, value);
|
||||
pfl->counter = value;
|
||||
pfl->wcycle++;
|
||||
pflash_blk_write_start(pfl, offset);
|
||||
break;
|
||||
case 0x60:
|
||||
if (cmd == 0xd0) {
|
||||
@@ -605,7 +581,12 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
switch (pfl->cmd) {
|
||||
case 0xe8: /* Block write */
|
||||
/* FIXME check @offset, @width */
|
||||
if (!pfl->ro && (pfl->blk_offset != -1)) {
|
||||
if (!pfl->ro) {
|
||||
/*
|
||||
* FIXME writing straight to memory is *wrong*. We
|
||||
* should write to a buffer, and flush it to memory
|
||||
* only on confirm command (see below).
|
||||
*/
|
||||
pflash_data_write(pfl, offset, value, width, be);
|
||||
} else {
|
||||
pfl->status |= 0x10; /* Programming error */
|
||||
@@ -614,8 +595,18 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
pfl->status |= 0x80;
|
||||
|
||||
if (!pfl->counter) {
|
||||
hwaddr mask = pfl->writeblock_size - 1;
|
||||
mask = ~mask;
|
||||
|
||||
trace_pflash_write(pfl->name, "block write finished");
|
||||
pfl->wcycle++;
|
||||
if (!pfl->ro) {
|
||||
/* Flush the entire write buffer onto backing storage. */
|
||||
/* FIXME premature! */
|
||||
pflash_update(pfl, offset & mask, pfl->writeblock_size);
|
||||
} else {
|
||||
pfl->status |= 0x10; /* Programming error */
|
||||
}
|
||||
}
|
||||
|
||||
pfl->counter--;
|
||||
@@ -627,17 +618,20 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset,
|
||||
case 3: /* Confirm mode */
|
||||
switch (pfl->cmd) {
|
||||
case 0xe8: /* Block write */
|
||||
if ((cmd == 0xd0) && !(pfl->status & 0x10)) {
|
||||
pflash_blk_write_flush(pfl);
|
||||
if (cmd == 0xd0) {
|
||||
/* FIXME this is where we should write out the buffer */
|
||||
pfl->wcycle = 0;
|
||||
pfl->status |= 0x80;
|
||||
} else {
|
||||
pflash_blk_write_abort(pfl);
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Aborting write to buffer not implemented,"
|
||||
" the data is already written to storage!\n"
|
||||
"Flash device reset into READ mode.\n",
|
||||
__func__);
|
||||
goto mode_read_array;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pflash_blk_write_abort(pfl);
|
||||
goto error_flash;
|
||||
}
|
||||
break;
|
||||
@@ -871,9 +865,6 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
|
||||
pfl->cmd = 0x00;
|
||||
pfl->status = 0x80; /* WSM ready */
|
||||
pflash_cfi01_fill_cfi_table(pfl);
|
||||
|
||||
pfl->blk_bytes = g_malloc(pfl->writeblock_size);
|
||||
pfl->blk_offset = -1;
|
||||
}
|
||||
|
||||
static void pflash_cfi01_system_reset(DeviceState *dev)
|
||||
@@ -893,8 +884,6 @@ static void pflash_cfi01_system_reset(DeviceState *dev)
|
||||
* This model deliberately ignores this delay.
|
||||
*/
|
||||
pfl->status = 0x80;
|
||||
|
||||
pfl->blk_offset = -1;
|
||||
}
|
||||
|
||||
static Property pflash_cfi01_properties[] = {
|
||||
|
||||
@@ -546,7 +546,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
}
|
||||
goto reset_flash;
|
||||
}
|
||||
trace_pflash_data_write(pfl->name, offset, width, value);
|
||||
trace_pflash_data_write(pfl->name, offset, width, value, 0);
|
||||
if (!pfl->ro) {
|
||||
p = (uint8_t *)pfl->storage + offset;
|
||||
if (pfl->be) {
|
||||
|
||||
@@ -12,8 +12,7 @@ fdctrl_tc_pulse(int level) "TC pulse: %u"
|
||||
pflash_chip_erase_invalid(const char *name, uint64_t offset) "%s: chip erase: invalid address 0x%" PRIx64
|
||||
pflash_chip_erase_start(const char *name) "%s: start chip erase"
|
||||
pflash_data_read(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x"
|
||||
pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x"
|
||||
pflash_data_write_block(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64
|
||||
pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64
|
||||
pflash_device_id(const char *name, uint16_t id) "%s: read device ID: 0x%04x"
|
||||
pflash_device_info(const char *name, uint64_t offset) "%s: read device information offset:0x%04" PRIx64
|
||||
pflash_erase_complete(const char *name) "%s: sector erase complete"
|
||||
@@ -33,9 +32,7 @@ pflash_unlock0_failed(const char *name, uint64_t offset, uint8_t cmd, uint16_t a
|
||||
pflash_unlock1_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: unlock0 failed 0x%" PRIx64 " 0x%02x"
|
||||
pflash_unsupported_device_configuration(const char *name, uint8_t width, uint8_t max) "%s: unsupported device configuration: device_width:%d max_device_width:%d"
|
||||
pflash_write(const char *name, const char *str) "%s: %s"
|
||||
pflash_write_block_start(const char *name, uint32_t value) "%s: block write start: bytes:0x%x"
|
||||
pflash_write_block_flush(const char *name) "%s: block write flush"
|
||||
pflash_write_block_abort(const char *name) "%s: block write abort"
|
||||
pflash_write_block(const char *name, uint32_t value) "%s: block write: bytes:0x%x"
|
||||
pflash_write_block_erase(const char *name, uint64_t offset, uint64_t len) "%s: block erase offset:0x%" PRIx64 " bytes:0x%" PRIx64
|
||||
pflash_write_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: command failed 0x%" PRIx64 " 0x%02x"
|
||||
pflash_write_invalid(const char *name, uint8_t cmd) "%s: invalid write for command 0x%02x"
|
||||
|
||||
@@ -65,7 +65,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
|
||||
iov_discard_undo(&req->inhdr_undo);
|
||||
iov_discard_undo(&req->outhdr_undo);
|
||||
virtqueue_push(req->vq, &req->elem, req->in_len);
|
||||
if (qemu_in_iothread()) {
|
||||
if (s->dataplane_started && !s->dataplane_disabled) {
|
||||
virtio_blk_data_plane_notify(s->dataplane, req->vq);
|
||||
} else {
|
||||
virtio_notify(vdev, req->vq);
|
||||
@@ -783,8 +783,7 @@ static void virtio_blk_handle_zone_report(VirtIOBlockReq *req,
|
||||
sizeof(struct virtio_blk_zone_report) +
|
||||
sizeof(struct virtio_blk_zone_descriptor)) {
|
||||
virtio_error(vdev, "in buffer too small for zone report");
|
||||
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
/* start byte offset of the zone report */
|
||||
|
||||
@@ -985,7 +985,8 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
port->bh = virtio_bh_new_guarded(dev, flush_queued_data_bh, port);
|
||||
port->bh = qemu_bh_new_guarded(flush_queued_data_bh, port,
|
||||
&dev->mem_reentrancy_guard);
|
||||
port->elem = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,6 @@ GlobalProperty hw_compat_5_2[] = {
|
||||
{ "PIIX4_PM", "smm-compat", "on"},
|
||||
{ "virtio-blk-device", "report-discard-granularity", "off" },
|
||||
{ "virtio-net-pci-base", "vectors", "3"},
|
||||
{ "nvme", "msix-exclusive-bar", "on"},
|
||||
};
|
||||
const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2);
|
||||
|
||||
|
||||
+3
-8
@@ -49,7 +49,6 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp)
|
||||
g_autofree CDATTableHeader *cdat_header = NULL;
|
||||
g_autofree CDATEntry *cdat_st = NULL;
|
||||
uint8_t sum = 0;
|
||||
uint8_t *hdr_buf;
|
||||
int ent, i;
|
||||
|
||||
/* Use default table if fopen == NULL */
|
||||
@@ -64,7 +63,7 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp)
|
||||
cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf,
|
||||
cdat->private);
|
||||
|
||||
if (cdat->built_buf_len <= 0) {
|
||||
if (!cdat->built_buf_len) {
|
||||
/* Build later as not all data available yet */
|
||||
cdat->to_update = true;
|
||||
return;
|
||||
@@ -96,12 +95,8 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp)
|
||||
/* For now, no runtime updates */
|
||||
cdat_header->sequence = 0;
|
||||
cdat_header->length += sizeof(CDATTableHeader);
|
||||
|
||||
hdr_buf = (uint8_t *)cdat_header;
|
||||
for (i = 0; i < sizeof(*cdat_header); i++) {
|
||||
sum += hdr_buf[i];
|
||||
}
|
||||
|
||||
sum += cdat_header->revision + cdat_header->sequence +
|
||||
cdat_header->length;
|
||||
/* Sum of all bytes including checksum must be 0 */
|
||||
cdat_header->checksum = ~sum + 1;
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ void cxl_component_register_block_init(Object *obj,
|
||||
/* io registers controls link which we don't care about in QEMU */
|
||||
memory_region_init_io(&cregs->io, obj, NULL, cregs, ".io",
|
||||
CXL2_COMPONENT_IO_REGION_SIZE);
|
||||
memory_region_init_io(&cregs->cache_mem, obj, &cache_mem_ops, cxl_cstate,
|
||||
memory_region_init_io(&cregs->cache_mem, obj, &cache_mem_ops, cregs,
|
||||
".cache_mem", CXL2_COMPONENT_CM_REGION_SIZE);
|
||||
|
||||
memory_region_add_subregion(&cregs->component_registers, 0, &cregs->io);
|
||||
|
||||
@@ -229,9 +229,12 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
|
||||
static uint64_t mdev_reg_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
CXLDeviceState *cxl_dstate = opaque;
|
||||
uint64_t retval = 0;
|
||||
|
||||
return cxl_dstate->memdev_status;
|
||||
retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MEDIA_STATUS, 1);
|
||||
retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MBOX_READY, 1);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void ro_reg_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
@@ -368,15 +371,7 @@ static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate)
|
||||
cxl_dstate->mbox_msi_n = msi_n;
|
||||
}
|
||||
|
||||
static void memdev_reg_init_common(CXLDeviceState *cxl_dstate)
|
||||
{
|
||||
uint64_t memdev_status_reg;
|
||||
|
||||
memdev_status_reg = FIELD_DP64(0, CXL_MEM_DEV_STS, MEDIA_STATUS, 1);
|
||||
memdev_status_reg = FIELD_DP64(memdev_status_reg, CXL_MEM_DEV_STS,
|
||||
MBOX_READY, 1);
|
||||
cxl_dstate->memdev_status = memdev_status_reg;
|
||||
}
|
||||
static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { }
|
||||
|
||||
void cxl_device_register_init_t3(CXLType3Dev *ct3d)
|
||||
{
|
||||
|
||||
@@ -181,7 +181,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
|
||||
memset(&info, 0, sizeof(info));
|
||||
ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
|
||||
#endif
|
||||
if (ret) {
|
||||
if (ret == -1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: illegal resource specified %d\n",
|
||||
__func__, ss.resource_id);
|
||||
|
||||
@@ -1463,8 +1463,10 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
|
||||
|
||||
g->ctrl_vq = virtio_get_queue(vdev, 0);
|
||||
g->cursor_vq = virtio_get_queue(vdev, 1);
|
||||
g->ctrl_bh = virtio_bh_new_guarded(qdev, virtio_gpu_ctrl_bh, g);
|
||||
g->cursor_bh = virtio_bh_new_guarded(qdev, virtio_gpu_cursor_bh, g);
|
||||
g->ctrl_bh = qemu_bh_new_guarded(virtio_gpu_ctrl_bh, g,
|
||||
&qdev->mem_reentrancy_guard);
|
||||
g->cursor_bh = qemu_bh_new_guarded(virtio_gpu_cursor_bh, g,
|
||||
&qdev->mem_reentrancy_guard);
|
||||
g->reset_bh = qemu_bh_new(virtio_gpu_reset_bh, g);
|
||||
qemu_cond_init(&g->reset_cond);
|
||||
QTAILQ_INIT(&g->reslist);
|
||||
|
||||
+19
-7
@@ -142,12 +142,12 @@ again:
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if ((req & (1u << dest_id)) == 0)
|
||||
size = 0;
|
||||
// if ((req & (1u << dest_id)) == 0)
|
||||
// size = 0;
|
||||
break;
|
||||
case 2:
|
||||
if ((req & (1u << src_id)) == 0)
|
||||
size = 0;
|
||||
// if ((req & (1u << src_id)) == 0)
|
||||
// size = 0;
|
||||
break;
|
||||
case 3:
|
||||
if ((req & (1u << src_id)) == 0
|
||||
@@ -155,8 +155,9 @@ again:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
if (!size)
|
||||
if (!size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Transfer one element. */
|
||||
/* ??? Should transfer multiple elements for a burst request. */
|
||||
@@ -179,6 +180,7 @@ again:
|
||||
ch->dest += swidth;
|
||||
}
|
||||
|
||||
//printf("Transfer size: %d, destination: 0x%08x\n", size, ch->dest);
|
||||
size--;
|
||||
ch->ctrl = (ch->ctrl & 0xfffff000) | size;
|
||||
if (size == 0) {
|
||||
@@ -204,6 +206,7 @@ again:
|
||||
ch->conf &= ~PL080_CCONF_E;
|
||||
}
|
||||
if (ch->ctrl & PL080_CCTRL_I) {
|
||||
//printf("Setting interrupt status of channel %d\n", c);
|
||||
s->tc_int |= 1 << c;
|
||||
}
|
||||
}
|
||||
@@ -212,6 +215,7 @@ again:
|
||||
if (--s->running)
|
||||
s->running = 1;
|
||||
}
|
||||
pl080_update(s);
|
||||
}
|
||||
|
||||
static uint64_t pl080_read(void *opaque, hwaddr offset,
|
||||
@@ -289,24 +293,30 @@ static void pl080_write(void *opaque, hwaddr offset,
|
||||
PL080State *s = (PL080State *)opaque;
|
||||
int i;
|
||||
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
if (offset >= 0x100 && offset < 0x200) {
|
||||
i = (offset & 0xe0) >> 5;
|
||||
if (i >= s->nchannels)
|
||||
goto bad_offset;
|
||||
switch ((offset >> 2) & 7) {
|
||||
case 0: /* SrcAddr */
|
||||
//printf("%s: setting source address of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].src = value;
|
||||
break;
|
||||
case 1: /* DestAddr */
|
||||
//printf("%s: setting destination address of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].dest = value;
|
||||
break;
|
||||
case 2: /* LLI */
|
||||
s->chan[i].lli = value;
|
||||
break;
|
||||
case 3: /* Control */
|
||||
//printf("%s: setting control of channel %d to 0x%08x (transfer size: %d)\n", __func__, i, value, value & 0xfff);
|
||||
s->chan[i].ctrl = value;
|
||||
break;
|
||||
case 4: /* Configuration */
|
||||
//printf("%s: setting configuration of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].conf = value;
|
||||
pl080_run(s);
|
||||
break;
|
||||
@@ -381,8 +391,10 @@ static void pl080_init(Object *obj)
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
PL080State *s = PL080(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
memory_region_init_io(&s->iomem1, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem1);
|
||||
memory_region_init_io(&s->iomem2, OBJECT(s), &pl080_ops, s, "pl080", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem2);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->interr);
|
||||
sysbus_init_irq(sbd, &s->inttc);
|
||||
|
||||
+10
-10
@@ -175,24 +175,24 @@ static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc,
|
||||
|
||||
switch (frag) {
|
||||
case 0:
|
||||
addr = (uint64_t)desc->source_address
|
||||
+ (extract64(desc->address_extension, 16, 16) << 32);
|
||||
addr = desc->source_address
|
||||
+ (extract32(desc->address_extension, 16, 12) << 20);
|
||||
break;
|
||||
case 1:
|
||||
addr = (uint64_t)desc->source_address2
|
||||
+ (extract64(desc->address_extension_23, 0, 16) << 32);
|
||||
addr = desc->source_address2
|
||||
+ (extract32(desc->address_extension_23, 0, 12) << 8);
|
||||
break;
|
||||
case 2:
|
||||
addr = (uint64_t)desc->source_address3
|
||||
+ (extract64(desc->address_extension_23, 16, 16) << 32);
|
||||
addr = desc->source_address3
|
||||
+ (extract32(desc->address_extension_23, 16, 12) << 20);
|
||||
break;
|
||||
case 3:
|
||||
addr = (uint64_t)desc->source_address4
|
||||
+ (extract64(desc->address_extension_45, 0, 16) << 32);
|
||||
addr = desc->source_address4
|
||||
+ (extract32(desc->address_extension_45, 0, 12) << 8);
|
||||
break;
|
||||
case 4:
|
||||
addr = (uint64_t)desc->source_address5
|
||||
+ (extract64(desc->address_extension_45, 16, 16) << 32);
|
||||
addr = desc->source_address5
|
||||
+ (extract32(desc->address_extension_45, 16, 12) << 20);
|
||||
break;
|
||||
default:
|
||||
addr = 0;
|
||||
|
||||
@@ -7,7 +7,6 @@ config HPPA_B160L
|
||||
select DINO
|
||||
select LASI
|
||||
select SERIAL
|
||||
select SERIAL_PCI
|
||||
select ISA_BUS
|
||||
select I8259
|
||||
select IDE_CMD646
|
||||
@@ -17,4 +16,3 @@ config HPPA_B160L
|
||||
select LASIPS2
|
||||
select PARALLEL
|
||||
select ARTIST
|
||||
select USB_OHCI_PCI
|
||||
+11
-17
@@ -36,8 +36,8 @@
|
||||
|
||||
#define MIN_SEABIOS_HPPA_VERSION 12 /* require at least this fw version */
|
||||
|
||||
#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10)
|
||||
static hwaddr soft_power_reg;
|
||||
/* Power button address at &PAGE0->pad[4] */
|
||||
#define HPA_POWER_BUTTON (0x40 + 4 * sizeof(uint32_t))
|
||||
|
||||
#define enable_lasi_lan() 0
|
||||
|
||||
@@ -45,6 +45,7 @@ static DeviceState *lasi_dev;
|
||||
|
||||
static void hppa_powerdown_req(Notifier *n, void *opaque)
|
||||
{
|
||||
hwaddr soft_power_reg = HPA_POWER_BUTTON;
|
||||
uint32_t val;
|
||||
|
||||
val = ldl_be_phys(&address_space_memory, soft_power_reg);
|
||||
@@ -220,7 +221,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus,
|
||||
fw_cfg_add_file(fw_cfg, "/etc/hppa/machine",
|
||||
g_memdup(mc->name, len), len);
|
||||
|
||||
val = cpu_to_le64(soft_power_reg);
|
||||
val = cpu_to_le64(HPA_POWER_BUTTON);
|
||||
fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr",
|
||||
g_memdup(&val, sizeof(val)), sizeof(val));
|
||||
|
||||
@@ -275,7 +276,6 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine)
|
||||
unsigned int smp_cpus = machine->smp.cpus;
|
||||
TranslateFn *translate;
|
||||
MemoryRegion *cpu_region;
|
||||
uint64_t ram_max;
|
||||
|
||||
/* Create CPUs. */
|
||||
for (unsigned int i = 0; i < smp_cpus; i++) {
|
||||
@@ -288,14 +288,10 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine)
|
||||
*/
|
||||
if (hppa_is_pa20(&cpu[0]->env)) {
|
||||
translate = translate_pa20;
|
||||
ram_max = 0xf0000000; /* 3.75 GB (limited by 32-bit firmware) */
|
||||
} else {
|
||||
translate = translate_pa10;
|
||||
ram_max = 0xf0000000; /* 3.75 GB (32-bit CPU) */
|
||||
}
|
||||
|
||||
soft_power_reg = translate(NULL, HPA_POWER_BUTTON);
|
||||
|
||||
for (unsigned int i = 0; i < smp_cpus; i++) {
|
||||
g_autofree char *name = g_strdup_printf("cpu%u-io-eir", i);
|
||||
|
||||
@@ -315,9 +311,9 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine)
|
||||
cpu_region);
|
||||
|
||||
/* Main memory region. */
|
||||
if (machine->ram_size > ram_max) {
|
||||
info_report("Max RAM size limited to %" PRIu64 " MB", ram_max / MiB);
|
||||
machine->ram_size = ram_max;
|
||||
if (machine->ram_size > 3 * GiB) {
|
||||
error_report("RAM size is currently restricted to 3GB");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1);
|
||||
|
||||
@@ -347,10 +343,8 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
|
||||
SysBusDevice *s;
|
||||
|
||||
/* SCSI disk setup. */
|
||||
if (drive_get_max_bus(IF_SCSI) >= 0) {
|
||||
dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
|
||||
lsi53c8xx_handle_legacy_cmdline(dev);
|
||||
}
|
||||
dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a"));
|
||||
lsi53c8xx_handle_legacy_cmdline(dev);
|
||||
|
||||
/* Graphics setup. */
|
||||
if (machine->enable_graphics && vga_interface_type != VGA_NONE) {
|
||||
@@ -363,7 +357,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
|
||||
}
|
||||
|
||||
/* Network setup. */
|
||||
if (nd_table[0].used && enable_lasi_lan()) {
|
||||
if (enable_lasi_lan()) {
|
||||
lasi_82596_init(addr_space, translate(NULL, LASI_LAN_HPA),
|
||||
qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA));
|
||||
}
|
||||
@@ -388,7 +382,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus,
|
||||
pci_set_word(&pci_dev->config[PCI_SUBSYSTEM_ID], 0x1227); /* Powerbar */
|
||||
|
||||
/* create a second serial PCI card when running Astro */
|
||||
if (serial_hd(1) && !lasi_dev) {
|
||||
if (!lasi_dev) {
|
||||
pci_dev = pci_new(-1, "pci-serial-4x");
|
||||
qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(1));
|
||||
qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(2));
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* IPod Touch I2C Bus Serial Interface Emulation
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co Ltd.
|
||||
* Maksim Kozlov, <m.kozlov@samsung.com>
|
||||
* Igor Mitsyanko, <i.mitsyanko@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/i2c/ipod_touch_i2c.h"
|
||||
|
||||
static void s5l8900_i2c_update(IPodTouchI2CState *s)
|
||||
{
|
||||
uint16_t level;
|
||||
level = (s->status & S5L8900_IICSTAT_START) &&
|
||||
(s->control & S5L8900_IICCON_IRQEN);
|
||||
|
||||
if (s->control & S5L8900_IICCON_IRQPEND)
|
||||
level = 0;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
|
||||
static int s5l8900_i2c_receive(IPodTouchI2CState *s)
|
||||
{
|
||||
int r;
|
||||
r = i2c_recv(s->bus);
|
||||
s5l8900_i2c_update(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int s5l8900_i2c_send(IPodTouchI2CState *s, uint8_t data)
|
||||
{
|
||||
if (!(s->status & S5L8900_IICSTAT_LASTBIT)) {
|
||||
s->status |= S5L8900_IICCON_ACKEN;
|
||||
s->data = data;
|
||||
s->iicreg20 |= 0x100;
|
||||
i2c_send(s->bus, s->data);
|
||||
}
|
||||
s5l8900_i2c_update(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* I2C read function */
|
||||
static uint64_t ipod_touch_i2c_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
|
||||
|
||||
//fprintf(stderr, "s5l8900_i2c_read(): offset = 0x%08x\n", offset);
|
||||
|
||||
switch (offset) {
|
||||
case I2CCON:
|
||||
return s->control;
|
||||
case I2CSTAT:
|
||||
return s->status;
|
||||
case I2CADD:
|
||||
return s->address;
|
||||
case I2CDS:
|
||||
s->iicreg20 |= 0x100;
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
return s->data;
|
||||
case I2CLC:
|
||||
return s->line_ctrl;
|
||||
case IICREG20:
|
||||
{
|
||||
// clear the flags
|
||||
uint32_t tmp_reg20 = s->iicreg20;
|
||||
s->iicreg20 &= ~0x100;
|
||||
s->iicreg20 &= ~0x2000;
|
||||
return tmp_reg20;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
//hw_error("s5l8900.i2c: bad read offset 0x" TARGET_FMT_plx "\n", offset);
|
||||
//fprintf(stderr, "%s: bad read offset 0x%08x\n", __func__, offset);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C write function */
|
||||
static void ipod_touch_i2c_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
|
||||
int mode;
|
||||
|
||||
//printf("s5l8900_i2c_write (base %d): offset = 0x%08x, val = 0x%08x\n", s->base, offset, value);
|
||||
|
||||
qemu_irq_lower(s->irq);
|
||||
|
||||
switch (offset) {
|
||||
case I2CCON:
|
||||
if(value & ~(S5L8900_IICCON_ACKEN)) {
|
||||
s->iicreg20 |= 0x100;
|
||||
}
|
||||
if((value & 0x10) && (s->status == 0x90)) {
|
||||
s->iicreg20 |= 0x2000;
|
||||
}
|
||||
s->control = value & 0xff;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
break;
|
||||
|
||||
case I2CSTAT:
|
||||
/* We have to make sure we don't miss an end transfer */
|
||||
if((!s->active) && ((s->status >> 6) != ((value >> 6)))) {
|
||||
s->status = value & 0xff;
|
||||
/* If they toggle the tx bit then we have to force an end transfer before mode update */
|
||||
} else if((s->active) && ((s->status >> 6) != ((value >> 6)))) {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active=0;
|
||||
s->status = value & 0xff;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
break;
|
||||
}
|
||||
mode = (s->status >> 6) & 0x3;
|
||||
if (value & S5L8900_IICSTAT_TXRXEN) {
|
||||
/* IIC-bus data output enable/disable bit */
|
||||
switch(mode) {
|
||||
case SR_MODE:
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
break;
|
||||
case ST_MODE:
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
break;
|
||||
case MR_MODE:
|
||||
if (value & S5L8900_IICSTAT_START) {
|
||||
/* START condition */
|
||||
s->status &= ~S5L8900_IICSTAT_LASTBIT;
|
||||
|
||||
s->iicreg20 |= 0x100;
|
||||
s->active = 1;
|
||||
i2c_start_transfer(s->bus, s->data >> 1, 1);
|
||||
} else {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active = 0;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
}
|
||||
break;
|
||||
case MT_MODE:
|
||||
if (value & S5L8900_IICSTAT_START) {
|
||||
/* START condition */
|
||||
s->status &= ~S5L8900_IICSTAT_LASTBIT;
|
||||
|
||||
s->iicreg20 |= 0x100;
|
||||
s->active = 1;
|
||||
i2c_start_transfer(s->bus, s->data >> 1, 0);
|
||||
} else {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active = 0;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
s5l8900_i2c_update(s);
|
||||
break;
|
||||
|
||||
case I2CADD:
|
||||
s->address = value & 0xff;
|
||||
break;
|
||||
|
||||
case I2CDS:
|
||||
s5l8900_i2c_send(s, value & 0xff);
|
||||
break;
|
||||
|
||||
case I2CLC:
|
||||
s->line_ctrl = value & 0xff;
|
||||
break;
|
||||
|
||||
case IICREG20:
|
||||
//s->iicreg20 &= ~value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
//hw_error("s5l8900.i2c: bad write offset 0x" TARGET_FMT_plx "\n", offset);
|
||||
//fprintf(stderr, "%s: bad write offset 0x%08x\n", __func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_i2c_ops = {
|
||||
.read = ipod_touch_i2c_read,
|
||||
.write = ipod_touch_i2c_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int i2c_index = 0;
|
||||
|
||||
static void ipod_touch_i2c_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchI2CState *s = IPOD_TOUCH_I2C(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_i2c_ops, s, TYPE_IPOD_TOUCH_I2C, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
char bus_name[5];
|
||||
sprintf(bus_name, "i2c%d", i2c_index);
|
||||
|
||||
s->bus = i2c_init_bus(dev, bus_name);
|
||||
i2c_index += 1;
|
||||
}
|
||||
|
||||
static void ipod_touch_i2c_reset(DeviceState *d)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_i2c_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->reset = ipod_touch_i2c_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_i2c_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_I2C,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchI2CState),
|
||||
.instance_init = ipod_touch_i2c_init,
|
||||
.class_init = ipod_touch_i2c_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_i2c_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_i2c_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_i2c_register_types)
|
||||
@@ -17,4 +17,5 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
|
||||
i2c_ss.add(when: 'CONFIG_IPOD_TOUCH_2G', if_true: files('ipod_touch_i2c.c'))
|
||||
system_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
|
||||
@@ -1415,7 +1415,7 @@ static void build_acpi0017(Aml *table)
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0017")));
|
||||
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(0x0B)));
|
||||
aml_append(method, aml_return(aml_int(0x01)));
|
||||
aml_append(dev, method);
|
||||
build_cxl_dsm_method(dev);
|
||||
|
||||
|
||||
+1
-1
@@ -34,5 +34,5 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms)
|
||||
|
||||
bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
|
||||
{
|
||||
return true;
|
||||
g_assert_not_reached();
|
||||
}
|
||||
+9
-6
@@ -8,10 +8,17 @@ config I8259
|
||||
config PL190
|
||||
bool
|
||||
|
||||
config PL192
|
||||
bool
|
||||
|
||||
config IOAPIC
|
||||
bool
|
||||
select I8259
|
||||
|
||||
config ARM_GIC
|
||||
bool
|
||||
select MSI_NONBROKEN
|
||||
|
||||
config OPENPIC
|
||||
bool
|
||||
select MSI_NONBROKEN
|
||||
@@ -21,18 +28,14 @@ config APIC
|
||||
select MSI_NONBROKEN
|
||||
select I8259
|
||||
|
||||
config ARM_GIC
|
||||
bool
|
||||
select ARM_GICV3_TCG if TCG
|
||||
select ARM_GIC_KVM if KVM
|
||||
select MSI_NONBROKEN
|
||||
|
||||
config ARM_GICV3_TCG
|
||||
bool
|
||||
default y
|
||||
depends on ARM_GIC && TCG
|
||||
|
||||
config ARM_GIC_KVM
|
||||
bool
|
||||
default y
|
||||
depends on ARM_GIC && KVM
|
||||
|
||||
config XICS
|
||||
|
||||
@@ -1,607 +0,0 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/intc/apple_aic.h"
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
/*
|
||||
* AIC splits IRQs into domains (ipid)
|
||||
* In T8030 device tree, we have aic->ipid_length = 72
|
||||
* => IRQ(extInts) max nr = ((len(ipid_mask)>>2)<<5) = 0x240 (interrupts)
|
||||
* -> num domains = (0x240 + 31)>>5 = 18 (domains)
|
||||
* 0x240/18 = 32 (bits) of an uint32_t
|
||||
*
|
||||
* Commands such as rAIC_EIR_MASK_SET/CLR assign each domain to a 32bit register.
|
||||
* When masking/unmasking-ing IRQ n,
|
||||
* write to (aic_base + command_reg_base + (n / 32) * 4)
|
||||
* a uint32_t which has (n % 32)-th bit set,
|
||||
* command_reg_base is 0x4100 for rAIC_EIR_MASK_SET, 0x4180 for rAIC_EIR_MASK_CLR.
|
||||
*
|
||||
* T8030 uses both fast IPI, and AIC IPIs.
|
||||
* AIC IPIs' vectors are right after IRQs' vectors.
|
||||
* num IRQ + (X * 2) -> self_ipi (cpuX->cpuX)
|
||||
* num IRQ + (Y * 2) + 1 -> other_ipi (cpuX->cpuY)
|
||||
*/
|
||||
|
||||
//TODO: this is hardcoded for T8030
|
||||
#define AIC_INT_COUNT (256)
|
||||
#define AIC_CPU_COUNT (2)
|
||||
#define AIC_VERSION (2)
|
||||
|
||||
#define rAIC_REV (0x0000)
|
||||
#define rAIC_CAP0 (0x0004)
|
||||
#define rAIC_CAP1 (0x0008)
|
||||
#define rAIC_RST (0x000C)
|
||||
|
||||
#define rAIC_GLB_CFG (0x0010)
|
||||
#define AIC_GLBCFG_IEN (1 << 0)
|
||||
#define AIC_GLBCFG_AEWT(_t) ((_t) << 4)
|
||||
#define AIC_GLBCFG_SEWT(_t) ((_t) << 8)
|
||||
#define AIC_GLBCFG_AIWT(_t) ((_t) << 12)
|
||||
#define AIC_GLBCFG_SIWT(_t) ((_t) << 16)
|
||||
#define AIC_GLBCFG_SYNC_ACG (1 << 29)
|
||||
#define AIC_GLBCFG_EIR_ACG (1 << 30)
|
||||
#define AIC_GLBCFG_REG_ACG (1 << 31)
|
||||
#define AIC_GLBCFG_WT_MASK (15)
|
||||
#define AIC_GLBCFG_WT_64MICRO (7)
|
||||
|
||||
#define rAIC_WHOAMI (0x2000)
|
||||
#define rAIC_IACK (0x2004)
|
||||
#define rAIC_IPI_SET (0x2008)
|
||||
#define rAIC_IPI_CLR (0x200C)
|
||||
#define AIC_IPI_NORMAL (1 << 0)
|
||||
#define AIC_IPI_SELF (1 << 31)
|
||||
#define rAIC_IPI_MASK_SET (0x2024)
|
||||
#define rAIC_IPI_MASK_CLR (0x2028)
|
||||
#define rAIC_IPI_DEFER_SET (0x202C)
|
||||
#define rAIC_IPI_DEFER_CLR (0x2030)
|
||||
|
||||
#define rAIC_EIR_DEST(_n) (0x3000 + ((_n) * 4))
|
||||
#define rAIC_EIR_SW_SET(_n) (0x4000 + ((_n) * 4))
|
||||
#define rAIC_EIR_SW_CLR(_n) (0x4080 + ((_n) * 4))
|
||||
#define rAIC_EIR_MASK_SET(_n) (0x4100 + ((_n) * 4))
|
||||
#define rAIC_EIR_MASK_CLR(_n) (0x4180 + ((_n) * 4))
|
||||
#define rAIC_EIR_INT_RO(_n) (0x4200 + ((_n) * 4))
|
||||
|
||||
#define rAIC_WHOAMI_Pn(_n) (0x5000 + ((_n) * 0x80))
|
||||
#define rAIC_IACK_Pn(_n) (0x5004 + ((_n) * 0x80))
|
||||
#define rAIC_IPI_SET_Pn(_n) (0x5008 + ((_n) * 0x80))
|
||||
#define rAIC_IPI_CLR_Pn(_n) (0x500C + ((_n) * 0x80))
|
||||
#define rAIC_IPI_MASK_SET_Pn(_n) (0x5024 + ((_n) * 0x80))
|
||||
#define rAIC_IPI_MASK_CLR_Pn(_n) (0x5028 + ((_n) * 0x80))
|
||||
#define rAIC_IPI_DEFER_SET_Pn(_n) (0x502C + ((_n) * 0x80))
|
||||
#define rAIC_IPI_DEFER_CLR_Pn(_n) (0x5030 + ((_n) * 0x80))
|
||||
|
||||
#define kAIC_INT_SPURIOUS (0x00000)
|
||||
#define kAIC_INT_EXT (0x10000)
|
||||
#define kAIC_INT_IPI (0x40000)
|
||||
#define kAIC_INT_IPI_NORM (0x40001)
|
||||
#define kAIC_INT_IPI_SELF (0x40002)
|
||||
|
||||
#define AIC_INT_EXT(_v) (((_v) & 0x70000) == kAIC_INT_EXT)
|
||||
#define AIC_INT_IPI(_v) (((_v) & 0x70000) == kAIC_INT_IPI)
|
||||
|
||||
#define AIC_INT_EXTID(_v) ((_v) & 0x3FF)
|
||||
|
||||
#define AIC_SRC_TO_EIR(_s) ((_s) >> 5)
|
||||
#define AIC_SRC_TO_MASK(_s) (1 << ((_s) & 0x1F))
|
||||
#define AIC_EIR_TO_SRC(_s, _v) (((_s) << 5) + ((_v) & 0x1F))
|
||||
|
||||
#define kAIC_MAX_EXTID (AIC_INT_COUNT)
|
||||
#define kAIC_VEC_IPI (kAIC_MAX_EXTID)
|
||||
#define kAIC_NUM_INTS (kAIC_VEC_IPI + 1)
|
||||
|
||||
#define kAIC_NUM_EIRS AIC_SRC_TO_EIR(kAIC_MAX_EXTID)
|
||||
|
||||
#define kAICWT 64000
|
||||
|
||||
#define kCNTFRQ (24000000)
|
||||
|
||||
static inline uint64_t apple_aic_emulate_timer(void)
|
||||
{
|
||||
int period_ns = NANOSECONDS_PER_SECOND > kCNTFRQ ?
|
||||
NANOSECONDS_PER_SECOND / kCNTFRQ : 1;
|
||||
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / period_ns;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check state and interrupt cpus, call with mutex locked
|
||||
*/
|
||||
static void apple_aic_update(AppleAICState *s)
|
||||
{
|
||||
uint32_t intr = 0;
|
||||
uint32_t potential = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
s->cpus[i].pendingIPI |= s->cpus[i].deferredIPI;
|
||||
s->cpus[i].deferredIPI = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
if ((s->cpus[i].pendingIPI & AIC_IPI_SELF) & (~s->cpus[i].ipi_mask)) {
|
||||
intr |= (1 << i);
|
||||
}
|
||||
if ((~s->cpus[i].ipi_mask & AIC_IPI_NORMAL)
|
||||
&& (s->cpus[i].pendingIPI & ((1 << s->numCPU) - 1))) {
|
||||
intr |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
i = -1;
|
||||
while ((i = find_next_bit((unsigned long *)s->eir_state, s->numIRQ, i+1))
|
||||
< s->numIRQ) {
|
||||
int dest;
|
||||
if ((test_bit(i, (unsigned long *)s->eir_mask) == 0)
|
||||
&& (dest = s->eir_dest[i])) {
|
||||
if (((intr & dest) == 0)) {
|
||||
/* The interrupt doesn't have a cpu that can process it yet */
|
||||
uint32_t cpu = find_first_bit((unsigned long *)&s->eir_dest[i],
|
||||
s->numCPU);
|
||||
intr |= (1 << cpu);
|
||||
potential |= dest;
|
||||
} else {
|
||||
int k;
|
||||
for (k = 0; k < s->numCPU; k++) {
|
||||
if (((intr & (1 << k)) == 0) && (potential & (1 << k))) {
|
||||
/*
|
||||
* cpu K isn't in the interrupt list
|
||||
* and can handle some of the previous interrupts
|
||||
*/
|
||||
intr |= (1 << k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
if (intr & (1 << i)) {
|
||||
qemu_irq_raise(s->cpus[i].irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_aic_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
AppleAICState *s = APPLE_AIC(opaque);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->mutex) {
|
||||
if (level) {
|
||||
set_bit(irq, (unsigned long *)s->eir_state);
|
||||
} else {
|
||||
clear_bit(irq, (unsigned long *)s->eir_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_aic_tick(void *opaque)
|
||||
{
|
||||
AppleAICState *s = APPLE_AIC(opaque);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->mutex) {
|
||||
apple_aic_update(s);
|
||||
}
|
||||
|
||||
timer_mod_ns(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + kAICWT);
|
||||
}
|
||||
|
||||
static void apple_aic_reset(DeviceState *dev)
|
||||
{
|
||||
int i;
|
||||
AppleAICState *s = APPLE_AIC(dev);
|
||||
|
||||
/* mask all IRQs */
|
||||
memset(s->eir_mask, 0xffff, sizeof(uint32_t) * s->numEIR);
|
||||
|
||||
#ifdef AIC_DEBUG_NEW_IRQ
|
||||
memset(s->eir_mask_once, 0xffff, sizeof(uint32_t) * s->numEIR);
|
||||
#endif
|
||||
|
||||
/* dest default to 0 */
|
||||
memset(s->eir_dest, 0, sizeof(uint32_t) * s->numIRQ);
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
/* mask all IPI */
|
||||
s->cpus[i].ipi_mask = AIC_IPI_NORMAL | AIC_IPI_SELF;
|
||||
s->cpus[i].pendingIPI = 0;
|
||||
s->cpus[i].deferredIPI = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_aic_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
AppleAICCPU *o = (AppleAICCPU *)opaque;
|
||||
AppleAICState *s = APPLE_AIC(o->aic);
|
||||
uint32_t val = (uint32_t)data;
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->mutex) {
|
||||
switch (addr) {
|
||||
case rAIC_RST:
|
||||
apple_aic_reset(DEVICE(s));
|
||||
break;
|
||||
|
||||
case rAIC_GLB_CFG:
|
||||
s->global_cfg = data;
|
||||
break;
|
||||
|
||||
case rAIC_IPI_SET:
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
if (val & (1 << i)) {
|
||||
set_bit(o->cpu_id, (unsigned long *)&s->cpus[i].pendingIPI);
|
||||
if (~s->cpus[i].ipi_mask & AIC_IPI_NORMAL) {
|
||||
qemu_irq_raise(s->cpus[i].irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (val & AIC_IPI_SELF) {
|
||||
o->pendingIPI |= AIC_IPI_SELF;
|
||||
if (~o->ipi_mask & AIC_IPI_SELF) {
|
||||
qemu_irq_raise(o->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_IPI_CLR:
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
if (val & (1 << i)) {
|
||||
clear_bit(o->cpu_id, (unsigned long *)&s->cpus[i].pendingIPI);
|
||||
}
|
||||
}
|
||||
|
||||
if (val & AIC_IPI_SELF) {
|
||||
o->pendingIPI &= ~AIC_IPI_SELF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_IPI_MASK_SET:
|
||||
o->ipi_mask |= (val & (AIC_IPI_NORMAL | AIC_IPI_SELF));
|
||||
break;
|
||||
|
||||
case rAIC_IPI_MASK_CLR:
|
||||
o->ipi_mask &= ~(val & (AIC_IPI_NORMAL | AIC_IPI_SELF));
|
||||
break;
|
||||
|
||||
case rAIC_IPI_DEFER_SET:
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
if (val & (1 << i)) {
|
||||
set_bit(o->cpu_id, (unsigned long *)&s->cpus[i].deferredIPI);
|
||||
}
|
||||
}
|
||||
|
||||
if (val & AIC_IPI_SELF) {
|
||||
o->deferredIPI |= AIC_IPI_SELF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_IPI_DEFER_CLR:
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
if (val & (1 << i)) {
|
||||
clear_bit(o->cpu_id, (unsigned long *)&s->cpus[i].deferredIPI);
|
||||
}
|
||||
}
|
||||
|
||||
if (val & AIC_IPI_SELF) {
|
||||
o->deferredIPI &= ~AIC_IPI_SELF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_EIR_DEST(0) ... rAIC_EIR_DEST(AIC_INT_COUNT):
|
||||
{
|
||||
uint32_t vector = (addr - rAIC_EIR_DEST(0)) / 4;
|
||||
if (unlikely(vector >= s->numIRQ)) {
|
||||
break;
|
||||
}
|
||||
s->eir_dest[vector] = val;
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_EIR_SW_SET(0) ... rAIC_EIR_SW_SET(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_SW_SET(0)) / 4;
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
s->eir_state[eir] |= val;
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_EIR_SW_CLR(0) ... rAIC_EIR_SW_CLR(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_SW_CLR(0)) / 4;
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
s->eir_state[eir] &= ~val;
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_EIR_MASK_SET(0) ... rAIC_EIR_MASK_SET(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_MASK_SET(0)) / 4;
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
s->eir_mask[eir] |= val;
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_EIR_MASK_CLR(0) ... rAIC_EIR_MASK_CLR(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_MASK_CLR(0)) / 4;
|
||||
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
|
||||
s->eir_mask[eir] &= ~val;
|
||||
|
||||
#ifdef AIC_DEBUG_NEW_IRQ
|
||||
if ((s->eir_mask[eir] | s->eir_mask_once[eir]) != s->eir_mask[eir]) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if ((s->eir_mask[eir] & (1 << i)) == 0 && (s->eir_mask_once[eir] & (1 << i)) != 0) {
|
||||
//trace_aic_new_irq(AIC_EIR_TO_SRC(eir, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
s->eir_mask_once[eir] &= s->eir_mask[eir];
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case rAIC_WHOAMI_Pn(0) ... rAIC_WHOAMI_Pn(AIC_CPU_COUNT) - 4:
|
||||
{
|
||||
uint32_t cpu = ((addr - 0x5000) / 0x80);
|
||||
if (unlikely(cpu >= s->numCPU)) {
|
||||
break;
|
||||
}
|
||||
addr = addr - 0x5000 + 0x2000 - 0x80 * cpu;
|
||||
qemu_mutex_unlock(&s->mutex);
|
||||
apple_aic_write(&s->cpus[cpu], addr, data, size);
|
||||
qemu_mutex_lock(&s->mutex);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
hw_error("AIC: Write to unspported reg 0x%08x cpu %u: 0x%x\n", addr, o->cpu_id, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t apple_aic_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
AppleAICCPU *o = (AppleAICCPU *)opaque;
|
||||
AppleAICState *s = APPLE_AIC(o->aic);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&s->mutex) {
|
||||
switch (addr) {
|
||||
case rAIC_REV:
|
||||
return 2;
|
||||
|
||||
case rAIC_CAP0:
|
||||
return (((uint64_t)s->numCPU - 1) << 16) | (s->numIRQ);
|
||||
|
||||
case rAIC_GLB_CFG:
|
||||
return s->global_cfg;
|
||||
|
||||
case rAIC_WHOAMI:
|
||||
return o->cpu_id;
|
||||
|
||||
case rAIC_IACK:
|
||||
{
|
||||
int i;
|
||||
|
||||
qemu_irq_lower(o->irq);
|
||||
if (o->pendingIPI & AIC_IPI_SELF & ~o->ipi_mask) {
|
||||
o->ipi_mask |= AIC_IPI_SELF;
|
||||
return kAIC_INT_IPI | kAIC_INT_IPI_SELF;
|
||||
}
|
||||
|
||||
if (~o->ipi_mask & AIC_IPI_NORMAL) {
|
||||
if (o->pendingIPI & ((1 << s->numCPU) - 1)) {
|
||||
o->ipi_mask |= AIC_IPI_NORMAL;
|
||||
return kAIC_INT_IPI | kAIC_INT_IPI_NORM;
|
||||
}
|
||||
}
|
||||
|
||||
i = -1;
|
||||
while ((i = find_next_bit((unsigned long *)s->eir_state,
|
||||
s->numIRQ, i+1)) < s->numIRQ) {
|
||||
if (test_bit(i, (unsigned long *)s->eir_mask) == 0) {
|
||||
if (s->eir_dest[i] & (1 << o->cpu_id)) {
|
||||
set_bit(i, (unsigned long *)s->eir_mask);
|
||||
return kAIC_INT_EXT | AIC_INT_EXTID(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return kAIC_INT_SPURIOUS;
|
||||
}
|
||||
|
||||
case rAIC_EIR_DEST(0) ... rAIC_EIR_DEST(AIC_INT_COUNT):
|
||||
{
|
||||
uint32_t vector = (addr - rAIC_EIR_DEST(0)) / 4;
|
||||
|
||||
if (unlikely(vector >= s->numIRQ)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return s->eir_dest[vector];
|
||||
}
|
||||
|
||||
case rAIC_EIR_MASK_SET(0) ... rAIC_EIR_MASK_SET(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_MASK_SET(0)) / 4;
|
||||
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return s->eir_mask[eir];
|
||||
}
|
||||
|
||||
case rAIC_EIR_MASK_CLR(0) ... rAIC_EIR_MASK_CLR(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_MASK_CLR(0)) / 4;
|
||||
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
|
||||
return s->eir_mask[eir];
|
||||
}
|
||||
|
||||
case rAIC_EIR_INT_RO(0) ... rAIC_EIR_INT_RO(kAIC_NUM_EIRS):
|
||||
{
|
||||
uint32_t eir = (addr - rAIC_EIR_INT_RO(0)) / 4;
|
||||
|
||||
if (unlikely(eir >= s->numEIR)) {
|
||||
break;
|
||||
}
|
||||
return s->eir_state[eir];
|
||||
}
|
||||
|
||||
case rAIC_WHOAMI_Pn(0) ... rAIC_WHOAMI_Pn(AIC_CPU_COUNT) - 4:
|
||||
{
|
||||
uint32_t cpu = ((addr - 0x5000) / 0x80);
|
||||
uint64_t val;
|
||||
|
||||
if (unlikely(cpu >= s->numCPU)) {
|
||||
break;
|
||||
}
|
||||
|
||||
addr = addr - 0x5000 + 0x2000 - 0x80 * cpu;
|
||||
qemu_mutex_unlock(&s->mutex);
|
||||
|
||||
val = apple_aic_read(&s->cpus[cpu], addr, size);
|
||||
qemu_mutex_lock(&s->mutex);
|
||||
return val;
|
||||
}
|
||||
default: {
|
||||
if (addr == 0x20) {
|
||||
return apple_aic_emulate_timer() & 0xFFFFFFFF;
|
||||
} else if (addr == 0x28) {
|
||||
return (apple_aic_emulate_timer() >> 32) & 0xFFFFFFFF;
|
||||
} else {
|
||||
hw_error("AIC: Read from unspported reg 0x%08x cpu: %u\n", addr, o->cpu_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps apple_aic_ops = {
|
||||
.read = apple_aic_read,
|
||||
.write = apple_aic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.valid.unaligned = false,
|
||||
};
|
||||
|
||||
static void apple_aic_realize(DeviceState *dev, struct Error **errp)
|
||||
{
|
||||
AppleAICState *s = APPLE_AIC(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
int i;
|
||||
|
||||
qemu_mutex_init(&s->mutex);
|
||||
s->cpus = g_new0(AppleAICCPU, s->numCPU);
|
||||
|
||||
for (i = 0; i < s->numCPU; i++) {
|
||||
AppleAICCPU *cpu = &s->cpus[i];
|
||||
|
||||
cpu->aic = s;
|
||||
cpu->cpu_id = i;
|
||||
memory_region_init_io(&cpu->iomem, OBJECT(dev), &apple_aic_ops, cpu, TYPE_APPLE_AIC, 0x6000);
|
||||
sysbus_init_mmio(sbd, &cpu->iomem);
|
||||
sysbus_init_irq(sbd, &cpu->irq);
|
||||
}
|
||||
|
||||
qdev_init_gpio_in(dev, apple_aic_set_irq, s->numIRQ);
|
||||
|
||||
assert(s->numCPU > 0);
|
||||
|
||||
s->eir_mask = g_new0(uint32_t, s->numEIR);
|
||||
s->eir_dest = g_new0(uint32_t, s->numIRQ);
|
||||
s->eir_state = g_new0(uint32_t, s->numEIR);
|
||||
|
||||
#ifdef AIC_DEBUG_NEW_IRQ
|
||||
s->eir_mask_once = g_new0(uint32_t, s->numEIR);
|
||||
#endif
|
||||
|
||||
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apple_aic_tick, dev);
|
||||
timer_mod_ns(s->timer, kAICWT);
|
||||
msi_nonbroken = true;
|
||||
}
|
||||
|
||||
static void apple_aic_unrealize(DeviceState *dev)
|
||||
{
|
||||
AppleAICState *s = APPLE_AIC(dev);
|
||||
timer_free(s->timer);
|
||||
}
|
||||
|
||||
AppleAICState *apple_aic_create()
|
||||
{
|
||||
DeviceState *dev;
|
||||
AppleAICState *s;
|
||||
|
||||
dev = qdev_new(TYPE_APPLE_AIC);
|
||||
s = APPLE_AIC(dev);
|
||||
s->numEIR = 8;
|
||||
s->numIRQ = s->numEIR * 32;
|
||||
s->numCPU = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void apple_aic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = apple_aic_realize;
|
||||
dc->unrealize = apple_aic_unrealize;
|
||||
dc->reset = apple_aic_reset;
|
||||
dc->desc = "Apple Interrupt Controller";
|
||||
}
|
||||
|
||||
static const TypeInfo apple_aic_info = {
|
||||
.name = TYPE_APPLE_AIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AppleAICState),
|
||||
.class_init = apple_aic_class_init,
|
||||
};
|
||||
|
||||
static void apple_aic_register_types(void)
|
||||
{
|
||||
type_register_static(&apple_aic_info);
|
||||
}
|
||||
|
||||
type_init(apple_aic_register_types);
|
||||
@@ -1067,7 +1067,7 @@ static uint64_t icc_hppir0_value(GICv3CPUState *cs, CPUARMState *env)
|
||||
*/
|
||||
bool irq_is_secure;
|
||||
|
||||
if (icc_no_enabled_hppi(cs)) {
|
||||
if (cs->hppi.prio == 0xff) {
|
||||
return INTID_SPURIOUS;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,7 @@ static uint64_t icc_hppir1_value(GICv3CPUState *cs, CPUARMState *env)
|
||||
*/
|
||||
bool irq_is_secure;
|
||||
|
||||
if (icc_no_enabled_hppi(cs)) {
|
||||
if (cs->hppi.prio == 0xff) {
|
||||
return INTID_SPURIOUS;
|
||||
}
|
||||
|
||||
@@ -1434,25 +1434,16 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
idx = icv_find_active(cs, irq);
|
||||
|
||||
if (idx < 0) {
|
||||
/*
|
||||
* No valid list register corresponding to EOI ID; if this is a vLPI
|
||||
* not in the list regs then do nothing; otherwise increment EOI count
|
||||
*/
|
||||
if (irq < GICV3_LPI_INTID_START) {
|
||||
icv_increment_eoicount(cs);
|
||||
}
|
||||
/* No valid list register corresponding to EOI ID */
|
||||
icv_increment_eoicount(cs);
|
||||
} else {
|
||||
uint64_t lr = cs->ich_lr_el2[idx];
|
||||
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
|
||||
int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp);
|
||||
|
||||
if (thisgrp == grp && lr_gprio == dropprio) {
|
||||
if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) {
|
||||
/*
|
||||
* Priority drop and deactivate not split: deactivate irq now.
|
||||
* LPIs always get their active state cleared immediately
|
||||
* because no separate deactivate is expected.
|
||||
*/
|
||||
if (!icv_eoi_split(env, cs)) {
|
||||
/* Priority drop and deactivate not split: deactivate irq now */
|
||||
icv_deactivate_irq(cs, idx);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,6 @@ system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files(
|
||||
'arm_gicv3_its.c',
|
||||
'arm_gicv3_redist.c',
|
||||
))
|
||||
system_ss.add(when: 'CONFIG_ATV4', if_true: files('apple_aic.c'))
|
||||
system_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c'))
|
||||
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_vic.c'))
|
||||
system_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c'))
|
||||
@@ -25,6 +24,7 @@ system_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'))
|
||||
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c'))
|
||||
system_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c'))
|
||||
system_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c'))
|
||||
system_ss.add(when: 'CONFIG_PL192', if_true: files('pl192.c'))
|
||||
system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_ic.c', 'bcm2836_control.c'))
|
||||
system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c'))
|
||||
system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c'))
|
||||
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* ARM PrimeCell PL192 Vector Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2009 Samsung Electronics.
|
||||
* Contributed by Kirill Batuzov <batuzovk@ispras.ru>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/intc/pl192.h"
|
||||
|
||||
extern CPUState *getMainCpuEnv(void);
|
||||
|
||||
|
||||
static void pl192_update(PL192State *);
|
||||
|
||||
static void pl192_raise(PL192State *s, int is_fiq)
|
||||
{
|
||||
if (is_fiq) {
|
||||
if (s->fiq) {
|
||||
/* Raise parent FIQ */
|
||||
qemu_irq_raise(s->fiq);
|
||||
} else {
|
||||
if (s->daisy) {
|
||||
/* FIQ is directly propagated through daisy chain */
|
||||
pl192_raise(s->daisy, is_fiq);
|
||||
} else {
|
||||
fprintf(stderr, "pl192i cannot raise FIQ. This usually means that "
|
||||
"initialization was done incorrectly.\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (s->irq) {
|
||||
/* Raise parent IRQ */
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
if (s->daisy) {
|
||||
/* Setup daisy input of the next chained contorller and force
|
||||
it to update it's state */
|
||||
s->daisy->daisy_vectaddr = s->address;
|
||||
s->daisy->daisy_callback = s;
|
||||
s->daisy->daisy_input = 1;
|
||||
pl192_update(s->daisy);
|
||||
} else {
|
||||
// TODO Needs urgent fixing!
|
||||
hw_error("pl192: cannot raise IRQ. This usually means that initialization was done incorrectly.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pl192_lower(PL192State *s, int is_fiq)
|
||||
{
|
||||
/* Lower parrent interrupt if there is one */
|
||||
if (is_fiq && s->fiq) {
|
||||
qemu_irq_lower(s->fiq);
|
||||
}
|
||||
if (!is_fiq && s->irq) {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
/* Propagate to the previous controller in chain if needed */
|
||||
if (s->daisy) {
|
||||
if (!is_fiq) {
|
||||
s->daisy->daisy_input = 0;
|
||||
pl192_update(s->daisy);
|
||||
} else {
|
||||
pl192_lower(s->daisy, is_fiq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find interrupt of the highest priority */
|
||||
static uint32_t pl192_priority_sorter(PL192State *s)
|
||||
{
|
||||
int i;
|
||||
uint32_t prio_irq[PL192_PRIO_LEVELS];
|
||||
|
||||
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
|
||||
prio_irq[i] = PL192_NO_IRQ;
|
||||
}
|
||||
if (s->daisy_input) {
|
||||
prio_irq[s->daisy_priority] = PL192_DAISY_IRQ;
|
||||
}
|
||||
for (i = PL192_INT_SOURCES - 1; i >= 0; i--) {
|
||||
if (s->irq_status & (1 << i)) {
|
||||
prio_irq[s->vect_priority[i]] = i;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
|
||||
if ((s->sw_priority_mask & (1 << i)) &&
|
||||
prio_irq[i] <= PL192_DAISY_IRQ) {
|
||||
return prio_irq[i];
|
||||
}
|
||||
}
|
||||
return PL192_NO_IRQ;
|
||||
}
|
||||
|
||||
static void pl192_update(PL192State *s)
|
||||
{
|
||||
/* TODO: does SOFTINT affects IRQ_STATUS??? */
|
||||
s->irq_status = (s->rawintr | s->softint) & s->intenable & ~s->intselect;
|
||||
s->fiq_status = (s->rawintr | s->softint) & s->intenable & s->intselect;
|
||||
if (s->fiq_status) {
|
||||
pl192_raise(s, 1);
|
||||
} else {
|
||||
pl192_lower(s, 1);
|
||||
}
|
||||
if (s->irq_status || s->daisy_input) {
|
||||
s->current_highest = pl192_priority_sorter(s);
|
||||
if (s->current_highest < PL192_INT_SOURCES) {
|
||||
s->address = s->vect_addr[s->current_highest];
|
||||
} else {
|
||||
s->address = s->daisy_vectaddr;
|
||||
}
|
||||
if (s->current_highest != s->current) {
|
||||
if (s->current_highest < PL192_INT_SOURCES) {
|
||||
if (s->vect_priority[s->current_highest] >= s->priority) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (s->current_highest == PL192_DAISY_IRQ) {
|
||||
if (s->daisy_priority >= s->priority) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (s->current_highest <= PL192_DAISY_IRQ) {
|
||||
pl192_raise(s, 0);
|
||||
} else {
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->current_highest = PL192_NO_IRQ;
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set priority level when an interrupt have been acknoledged by CPU.
|
||||
Also save interrupt id and priority to stack so it can be restored
|
||||
lately. */
|
||||
static inline void pl192_mask_priority(PL192State *s)
|
||||
{
|
||||
if (s->stack_i >= PL192_INT_SOURCES) {
|
||||
hw_error("pl192: internal error (trying to mask when there are no more sources)\n");
|
||||
}
|
||||
s->stack_i++;
|
||||
if (s->current == PL192_DAISY_IRQ) {
|
||||
s->priority = s->daisy_priority;
|
||||
} else {
|
||||
s->priority = s->vect_priority[s->current];
|
||||
}
|
||||
s->priority_stack[s->stack_i] = s->priority;
|
||||
s->irq_stack[s->stack_i] = s->current;
|
||||
}
|
||||
|
||||
/* Set priority level when interrupt have been successfully processed by CPU.
|
||||
Also restore previous interrupt id and priority level. */
|
||||
static inline void pl192_unmask_priority(PL192State *s)
|
||||
{
|
||||
if (s->stack_i < 1) {
|
||||
return; // simply ignore this event
|
||||
//hw_error("pl192: internal error (mask stack insufficient)\n");
|
||||
}
|
||||
s->stack_i--;
|
||||
s->priority = s->priority_stack[s->stack_i];
|
||||
s->current = s->irq_stack[s->stack_i];
|
||||
}
|
||||
|
||||
/* IRQ was acknoledged by CPU. Update controller state accordingly */
|
||||
static uint32_t pl192_irq_ack(PL192State *s)
|
||||
{
|
||||
int is_daisy = (s->current_highest == PL192_DAISY_IRQ);
|
||||
uint32_t res = s->address;
|
||||
|
||||
s->current = s->current_highest;
|
||||
pl192_mask_priority(s);
|
||||
if (is_daisy) {
|
||||
pl192_mask_priority(s->daisy_callback);
|
||||
}
|
||||
pl192_update(s);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* IRQ was processed by CPU. Update controller state accrodingly */
|
||||
static void pl192_irq_fin(PL192State *s)
|
||||
{
|
||||
int is_daisy = (s->current == PL192_DAISY_IRQ);
|
||||
|
||||
pl192_unmask_priority(s);
|
||||
if (is_daisy) {
|
||||
pl192_unmask_priority(s->daisy_callback);
|
||||
}
|
||||
pl192_update(s);
|
||||
|
||||
/* hmm is this right?*/
|
||||
/*
|
||||
if (s->current == PL192_NO_IRQ && (s->current_highest >= PL192_INT_SOURCES)) {
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static uint64_t pl192_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (offset & 3) {
|
||||
fprintf(stderr, "pl192: bad read offset (1) " HWADDR_FMT_plx "\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
unsigned char pl192_id[] = { 0x92, 0x11, 0x04, 0x00, 0x0D, 0xF0, 0x05, 0xB1 };
|
||||
return pl192_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
if (offset >= 0x100 && offset < 0x180) {
|
||||
return s->vect_addr[(offset - 0x100) >> 2];
|
||||
}
|
||||
if (offset >= 0x200 && offset < 0x280) {
|
||||
return s->vect_priority[(offset - 0x200) >> 2];
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case PL192_IRQSTATUS:
|
||||
//fprintf(stderr, "%s: irqstatus 0x%08x\n", __FUNCTION__, s->irq_status);
|
||||
return s->irq_status;
|
||||
case PL192_FIQSTATUS:
|
||||
return s->fiq_status;
|
||||
case PL192_RAWINTR:
|
||||
return s->rawintr;
|
||||
case PL192_INTSELECT:
|
||||
return s->intselect;
|
||||
case PL192_INTENABLE:
|
||||
return s->intenable;
|
||||
case PL192_SOFTINT:
|
||||
return s->softint;
|
||||
case PL192_PROTECTION:
|
||||
return s->protection;
|
||||
case PL192_SWPRIORITYMASK:
|
||||
return s->sw_priority_mask;
|
||||
case PL192_PRIORITYDAISY:
|
||||
return s->daisy_priority;
|
||||
case PL192_INTENCLEAR:
|
||||
return 0;
|
||||
case PL192_SOFTINTCLEAR:
|
||||
fprintf(stderr, "pl192: attempt to read write-only register (offset = "
|
||||
HWADDR_FMT_plx ")\n", offset);
|
||||
case PL192_VECTADDR:
|
||||
return pl192_irq_ack(s);
|
||||
/* Workaround for kernel code using PL190 */
|
||||
case PL190_ITCR:
|
||||
case PL190_VECTADDR:
|
||||
case PL190_DEFVECTADDR:
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "pl192: bad read offset (2) " HWADDR_FMT_plx "\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl192_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (offset & 3) {
|
||||
hw_error("pl192: bad write offset (1) " HWADDR_FMT_plx "\n", offset);
|
||||
}
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
hw_error("pl192: attempt to write to a read-only register (offset = "
|
||||
HWADDR_FMT_plx ")\n", offset);
|
||||
}
|
||||
if (offset >= 0x100 && offset < 0x180) {
|
||||
s->vect_addr[(offset - 0x100) >> 2] = value;
|
||||
pl192_update(s);
|
||||
return;
|
||||
}
|
||||
if (offset >= 0x200 && offset < 0x280) {
|
||||
s->vect_priority[(offset - 0x200) >> 2] = value & 0xf;
|
||||
pl192_update(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case PL192_IRQSTATUS:
|
||||
/* This is a readonly register, but linux tries to write to it
|
||||
anyway. Ignore the write. */
|
||||
return;
|
||||
case PL192_FIQSTATUS:
|
||||
case PL192_RAWINTR:
|
||||
hw_error("pl192: attempt to write to a read-only register (offset = "
|
||||
HWADDR_FMT_plx ")\n", offset);
|
||||
break;
|
||||
case PL192_INTSELECT:
|
||||
s->intselect = value;
|
||||
break;
|
||||
case PL192_INTENABLE:
|
||||
s->intenable |= value;
|
||||
break;
|
||||
case PL192_INTENCLEAR:
|
||||
s->intenable &= ~value;
|
||||
break;
|
||||
case PL192_SOFTINT:
|
||||
s->softint |= value;
|
||||
break;
|
||||
case PL192_SOFTINTCLEAR:
|
||||
s->softint &= ~value;
|
||||
break;
|
||||
case PL192_PROTECTION:
|
||||
/* TODO: implement protection */
|
||||
s->protection = value & 1;
|
||||
break;
|
||||
case PL192_SWPRIORITYMASK:
|
||||
s->sw_priority_mask = value & 0xffff;
|
||||
break;
|
||||
case PL192_PRIORITYDAISY:
|
||||
s->daisy_priority = value & 0xf;
|
||||
break;
|
||||
case PL192_VECTADDR:
|
||||
pl192_irq_fin(s);
|
||||
return;
|
||||
case PL190_ITCR:
|
||||
case PL190_VECTADDR:
|
||||
case PL190_DEFVECTADDR:
|
||||
/* NB: This thing is not present here, but linux wants to write it */
|
||||
/* Ignore written value */
|
||||
return;
|
||||
default:
|
||||
fprintf(stderr, "pl192: bad write offset (2) " HWADDR_FMT_plx "\n", offset);
|
||||
return;
|
||||
}
|
||||
|
||||
pl192_update(s);
|
||||
}
|
||||
|
||||
static void pl192_irq_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (level) {
|
||||
s->rawintr |= 1 << irq;
|
||||
} else {
|
||||
s->rawintr &= ~(1 << irq);
|
||||
}
|
||||
pl192_update(opaque);
|
||||
}
|
||||
|
||||
static void pl192_reset(DeviceState *d)
|
||||
{
|
||||
PL192State *s = PL192(d);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PL192_INT_SOURCES; i++) {
|
||||
s->vect_priority[i] = 0xf;
|
||||
}
|
||||
s->sw_priority_mask = 0xffff;
|
||||
s->daisy_priority = 0xf;
|
||||
s->current = PL192_NO_IRQ;
|
||||
s->current_highest = PL192_NO_IRQ;
|
||||
s->stack_i = 0;
|
||||
s->priority_stack[0] = 0x10;
|
||||
s->irq_stack[0] = PL192_NO_IRQ;
|
||||
s->priority = 0x10;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pl192_ops = {
|
||||
.read = pl192_read,
|
||||
.write = pl192_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
DeviceState *pl192_manual_init(char *mem_name, ...)
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_PL192);
|
||||
PL192State *s = PL192(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
va_list va;
|
||||
qemu_irq irq;
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pl192_ops, s, mem_name, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->fiq);
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
|
||||
va_start(va, mem_name);
|
||||
int n = 0;
|
||||
while (1) {
|
||||
irq = va_arg(va, qemu_irq);
|
||||
if (!irq) {
|
||||
break;
|
||||
}
|
||||
sysbus_connect_irq(sbd, n, irq);
|
||||
n++;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void pl192_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
PL192State *s = PL192(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
//memory_region_init_io(&s->iomem, obj, &pl192_ops, s, "pl192", 0x1000);
|
||||
//sysbus_init_mmio(sbd, &s->iomem);
|
||||
//qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
|
||||
//sysbus_init_irq(sbd, s->irq);
|
||||
//sysbus_init_irq(sbd, s->fiq);
|
||||
}
|
||||
|
||||
static void pl192_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = pl192_reset;
|
||||
//dc->vmsd = &vmstate_pl192;
|
||||
// TODO save VM
|
||||
}
|
||||
|
||||
static const TypeInfo pl192_info = {
|
||||
.name = TYPE_PL192,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PL192State),
|
||||
.instance_init = pl192_init,
|
||||
.class_init = pl192_class_init,
|
||||
};
|
||||
|
||||
static void pl192_register_types(void)
|
||||
{
|
||||
type_register_static(&pl192_info);
|
||||
}
|
||||
|
||||
type_init(pl192_register_types)
|
||||
@@ -463,7 +463,6 @@ static uint32_t riscv_aplic_idc_claimi(RISCVAPLICState *aplic, uint32_t idc)
|
||||
|
||||
if (!topi) {
|
||||
aplic->iforce[idc] = 0;
|
||||
riscv_aplic_idc_update(aplic, idc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -613,7 +613,7 @@ void via_isa_set_irq(PCIDevice *d, int pin, int level)
|
||||
ViaISAState *s = VIA_ISA(pci_get_function_0(d));
|
||||
uint8_t irq = d->config[PCI_INTERRUPT_LINE], max_irq = 15;
|
||||
int f = PCI_FUNC(d->devfn);
|
||||
uint16_t mask;
|
||||
uint16_t mask = BIT(f);
|
||||
|
||||
switch (f) {
|
||||
case 0: /* PIRQ/PINT inputs */
|
||||
@@ -628,7 +628,6 @@ void via_isa_set_irq(PCIDevice *d, int pin, int level)
|
||||
}
|
||||
|
||||
/* Keep track of the state of all sources */
|
||||
mask = BIT(f);
|
||||
if (level) {
|
||||
s->irq_state[0] |= mask;
|
||||
} else {
|
||||
|
||||
Loaded 100 of 376 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user