Compare commits

...
Author SHA1 Message Date
Martijn de Vos ad39d6475a Merge pull request #103 from MrDiagnose/patch-1
Update RUNNING.md
2023-12-24 20:18:30 +01:00
MrDiagnose 87dafcd4ad Update RUNNING.md
updated build instructions for linux & windows
2023-12-24 23:02:38 +05:30
Martijn de Vos be55c408ce Update RUNNING.md 2023-12-24 10:11:07 +01:00
Martijn de Vos 09e689fce1 Update README.md with a schematic 2023-12-16 17:25:20 +01:00
Martijn de Vos c6f78c8263 Merge pull request #93 from jvyden/ipod-2g-file-error-handling
Check if Bootrom/NOR/NAND files exist before attempting to use
2023-12-16 11:23:46 +01:00
jvyden b4aacff740 Fix NAND path check 2023-12-14 20:45:44 -05:00
jvyden 04eeb538d1 Check if Bootrom/NOR/NAND files exist before attempting to use 2023-12-14 20:30:52 -05:00
Martijn de Vos 26bdb75cd2 Merge pull request #82 from spetterman66/ipod_touch_2g
Make qemu-ios easier to compile on Microsoft Windows
2023-12-02 09:13:55 +01:00
Martijn de Vos aaee2c4dbf Deleted workflow from IT2G branch 2023-11-28 09:04:17 +01:00
Ryan 65a80f936e Include disablement of the stack protector in the configure script
This commit is intended to address the issue of stack smashing being detected when running QEMU-IOS on Microsoft Windows built with MINGW64, which causes the emulator to be unable to run.
2023-11-23 20:11:36 +01:00
Ryan 6c18b04f3c Switch position of "extern" and "G_NORETURN"
Please read https://www.mail-archive.com/qemu-devel@nongnu.org/msg950158.html, it fixes a bug in compilation on Microsoft Windows.
2023-11-23 14:42:39 +01:00
Ryan ce0ce0a64e Different file paths on Microsoft Windows 2023-11-23 14:40:39 +01:00
Martijn de Vos 1ad020b0dc Merge pull request #68 from breakgimme/patch-1
Fix the configure command for apple silicon macs in RUNNING.MD
2023-11-09 08:54:59 +01:00
breakgimme 93121ee2bb fix the configure command for apple silicon macs 2023-11-08 22:02:17 +01:00
Martijn de Vos 472781b587 Update README.md
Added image
2023-11-08 21:27:06 +01:00
Martijn de Vos 374d3a330f Changed README.md 2023-11-08 21:24:22 +01:00
Martijn de Vos 817beeefa8 Added run instructions 2023-11-08 21:18:52 +01:00
Martijn de Vos 6024cbc983 Made paths flexible 2023-11-08 21:18:14 +01:00
Martijn de Vos 4a95aeacb3 Removed print statement 2023-11-08 19:49:54 +01:00
Martijn de Vos 9390b5a38c Home/power buttons working 2023-11-04 14:08:38 +01:00
Martijn de Vos 8b4c1455b7 Prevent GUI grab 2023-11-03 18:16:18 +01:00
Martijn de Vos 603dafdb84 LCD + Multitouch working!! 2023-11-03 18:02:38 +01:00
Martijn de Vos 0bdff11572 Worked on TVOut 2023-09-08 17:29:18 +02:00
Martijn de Vos cc21020f64 Started working on TVOut
This is blocking starting the display server
2023-09-06 21:55:35 +02:00
Martijn de Vos f7b6729f88 Device is activated! 2023-09-06 19:53:45 +02:00
Martijn de Vos 94972bf6c8 BCM4325 driver gets past initialization!! 2023-09-04 21:43:19 +02:00
Martijn de Vos 182f275929 More work on SDIO/BCM4325 2023-09-03 14:45:07 +02:00
Martijn de Vos cbc8c89420 Got SDIO IRQ working 2023-09-01 21:28:35 +02:00
Martijn de Vos 86be721bbc Got a tiny bit further in the boot process 2023-08-30 19:03:20 +02:00
Martijn de Vos ee22d61aa7 Made some progress with the BCM4325 2023-08-29 22:07:49 +02:00
Martijn de Vos 8ed16c6eb7 Merge branch 'ipod_touch_2g' of github.com:devos50/qemu-ios into ipod_touch_2g 2023-08-12 10:57:52 +02:00
Martijn de Vos 0a48ea3de8 Started working on SDIO 2023-08-12 10:57:15 +02:00
Martijn de Vos 9836218db1 Merge pull request #55 from zoe-vb/ipod_touch_2g
Update ipod_touch_fmss.c
2023-08-11 08:59:34 +02:00
zoe-vb c3c1830210 Update ipod_touch_fmss.c
fix issue where boot arguments were not actually updating when recompiling qemu.
2023-08-11 00:02:34 +01:00
Martijn de Vos 37f8199e52 Booting up until lockdownd! 2023-07-27 19:44:17 +02:00
Martijn de Vos dd673b69f5 Booting SpringBoard 2023-07-27 16:28:07 +02:00
Martijn de Vos 1744cb29cf Multiple fixes for SPI 2023-07-12 20:38:20 +02:00
Martijn de Vos 6c4f881436 Several fixes and changes 2023-07-08 16:43:02 +02:00
Martijn de Vos 9a2d42ca33 Got NOR up and running on both SPI0 and SPI1 2023-07-07 22:48:18 +02:00
Martijn de Vos 288942882a Mikey is loading 2023-07-07 20:34:58 +02:00
Martijn de Vos 0b74ae5cfd Got some more stuff running 2023-07-05 22:04:03 +02:00
Martijn de Vos 5b185bc4fe Got more peripherals up and running! 2023-07-03 20:48:22 +02:00
Martijn de Vos f260d8455f Added workaround for device tree and several memory mappings 2023-07-02 18:19:26 +02:00
Martijn de Vos ac405b5f41 Able to load kernel, with some hacks! 2023-07-01 20:45:49 +02:00
Martijn de Vos 1f85f989cb Circumventing NAND 2023-06-18 15:16:33 +02:00
Martijn de Vos 4ba00929a0 Implemented missing registers 2023-06-12 20:18:52 +02:00
Martijn de Vos fa67c82cab Got the NAND working a bit more 2023-02-12 17:56:47 +01:00
Martijn de Vos 999255e508 Got some of the NAND initializations working 2023-02-12 09:14:08 +01:00
Martijn de Vos 463e8d8047 Worked on several components 2023-02-07 19:59:57 +01:00
Martijn de Vos 3e17ca75b6 Some work on LCD/MIPI DSI 2023-01-31 16:35:44 +01:00
Martijn de Vos 4f7625e4cb Hardcoded iBoot decryption keys 2023-01-30 18:17:53 +01:00
Martijn de Vos 77da53507a Working on iBoot 2023-01-13 13:03:51 +01:00
Martijn de Vos b2b7110aef Fixed SPI again 2023-01-08 21:51:23 +01:00
Martijn de Vos c386a81c40 Worked on LLB 2023-01-08 17:20:43 +01:00
Martijn de Vos 07725f5d22 AES engine is correctly decrypting LLB payload 2023-01-07 16:31:41 +01:00
Martijn de Vos 91b22b8cbe Fixed various things 2022-12-28 15:46:33 +01:00
Martijn de Vos d368c00670 PKE engine seems to be operational 2022-12-28 13:17:12 +01:00
Martijn de Vos 7016eae2e6 Implemented a first version of the PKE engine 2022-12-27 22:18:59 +01:00
Martijn de Vos a8aa1cc8d8 NOR SPI seems to work 2022-12-26 15:07:27 +01:00
Martijn de Vos 5ed081ec42 S5L8720 SPI seems to be functional 2022-12-25 23:38:03 +01:00
Martijn de Vos bcb4f83561 Added NOR SPI peripheral 2022-12-25 09:15:45 +01:00
Martijn de Vos fefcf90209 More work on iPod Touch 2G emulation 2022-12-24 15:59:19 +01:00
Martijn de Vos e01813b60e Started working on iPod Touch 2G 2022-12-23 23:05:42 +01:00
68 changed files with 6981 additions and 210 deletions

No files matched your search

-30
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 --enable-sdl --disable-cocoa --target-list=arm-softmmu --disable-capstone --disable-pie --disable-slirp --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).
+1
View File
@@ -42,3 +42,4 @@ CONFIG_FSL_IMX6UL=y
CONFIG_SEMIHOSTING=y
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
CONFIG_ALLWINNER_H3=y
CONFIG_IPOD_TOUCH_2G=y
+5
View File
@@ -568,3 +568,8 @@ config ARMSSE
select UNIMP
select SSE_COUNTER
select SSE_TIMER
config IPOD_TOUCH_2G
bool
select IPOD_TOUCH_2G
select PL192
+534
View File
@@ -0,0 +1,534 @@
#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"
#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)
+203
View File
@@ -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)
+58
View File
@@ -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)
+55
View File
@@ -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)
+156
View File
@@ -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)
+199
View File
@@ -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)
+62
View File
@@ -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)
+218
View File
@@ -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)
+55
View File
@@ -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)
+152
View File
@@ -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)
+92
View File
@@ -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)
+414
View File
@@ -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)
+138
View File
@@ -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)
+104
View File
@@ -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)
+158
View File
@@ -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)
+54
View File
@@ -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)
+252
View File
@@ -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)
+153
View File
@@ -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)
+328
View File
@@ -0,0 +1,328 @@
/*
* S5L8900 SPI Emulation
*
* by cmw
*/
#include "hw/arm/ipod_touch_spi.h"
static int apple_spi_word_size(IPodTouchSPIState *s)
{
switch (R_CFG_WORD_SIZE(REG(s, R_CFG))) {
case R_CFG_WORD_SIZE_8B:
return 1;
case R_CFG_WORD_SIZE_16B:
return 2;
case R_CFG_WORD_SIZE_32B:
return 4;
default:
break;
}
g_assert_not_reached();
}
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(IPodTouchSPIState *s)
{
if (!fifo8_is_empty(&s->rx_fifo)) {
REG(s, R_STATUS) |= R_STATUS_RXREADY;
}
}
static void apple_spi_update_irq(IPodTouchSPIState *s)
{
uint32_t irq = 0;
uint32_t mask = 0;
if (REG(s, R_CFG) & R_CFG_IE_RXREADY) {
mask |= R_STATUS_RXREADY;
}
if (REG(s, R_CFG) & R_CFG_IE_TXEMPTY) {
mask |= R_STATUS_TXEMPTY;
}
if (REG(s, R_CFG) & R_CFG_IE_COMPLETE) {
mask |= R_STATUS_COMPLETE;
}
if (REG(s, R_STATUS) & mask) {
irq = 1;
}
if (irq != s->last_irq) {
s->last_irq = irq;
qemu_set_irq(s->irq, irq);
}
}
static void apple_spi_update_cs(IPodTouchSPIState *s)
{
BusState *b = BUS(s->spi);
BusChild *kid = QTAILQ_FIRST(&b->children);
if (kid) {
// TODO GPIO not properly setup yet
//qemu_set_irq(qdev_get_gpio_in_named(kid->child, SSI_GPIO_CS, 0), (REG(s, R_PIN) & R_PIN_CS) != 0);
}
}
static void apple_spi_cs_set(void *opaque, int pin, int level)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
if (level) {
REG(s, R_PIN) |= R_PIN_CS;
} else {
REG(s, R_PIN) &= ~R_PIN_CS;
}
apple_spi_update_cs(s);
}
static void apple_spi_run(IPodTouchSPIState *s)
{
uint32_t tx;
uint32_t rx;
if (!(REG(s, R_CTRL) & R_CTRL_RUN)) {
return;
}
if (REG(s, R_RXCNT) == 0 && REG(s, R_TXCNT) == 0) {
return;
}
apple_spi_update_xfer_tx(s);
//printf("TX queue: %d, RX queue: %d\n", REG(s, R_TXCNT), REG(s, R_RXCNT));
//printf("TX buffer size: %d, RX buffer size: %d\n", fifo8_num_used(&s->tx_fifo), fifo8_num_used(&s->rx_fifo));
while (REG(s, R_TXCNT) && !fifo8_is_empty(&s->tx_fifo)) {
tx = (uint32_t)fifo8_pop(&s->tx_fifo);
rx = ssi_transfer(s->spi, tx);
REG(s, R_TXCNT)--;
apple_spi_update_xfer_tx(s);
if (REG(s, R_RXCNT) > 0) {
if (fifo8_is_full(&s->rx_fifo)) {
hw_error("Rx buffer overflow: %d\n", fifo8_num_free(&s->rx_fifo));
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
} else {
fifo8_push(&s->rx_fifo, (uint8_t)rx);
REG(s, R_RXCNT)--;
apple_spi_update_xfer_rx(s);
}
}
}
// fetch the remaining bytes by sending sentinel bytes.
while (!fifo8_is_full(&s->rx_fifo) && (REG(s, R_RXCNT) > 0) && (REG(s, R_CFG) & R_CFG_AGD)) {
rx = ssi_transfer(s->spi, 0xff);
if (fifo8_is_full(&s->rx_fifo)) {
hw_error("Rx buffer overflow: %d\n", fifo8_num_free(&s->rx_fifo));
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
break;
} else {
fifo8_push(&s->rx_fifo, (uint8_t)rx);
REG(s, R_RXCNT)--;
apple_spi_update_xfer_rx(s);
}
}
if (REG(s, R_RXCNT) == 0 && REG(s, R_TXCNT) == 0) {
REG(s, R_STATUS) |= R_STATUS_COMPLETE;
}
//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 ipod_touch_spi_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(opaque);
//printf("%s (base %d): read from location 0x%08x\n", __func__, s->base, addr);
uint32_t r;
bool run = false;
r = s->regs[addr >> 2];
switch (addr) {
case R_RXDATA: {
const uint8_t *buf = NULL;
int word_size = apple_spi_word_size(s);
uint32_t num = 0;
if (fifo8_is_empty(&s->rx_fifo)) {
hw_error("Rx buffer underflow\n");
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx underflow\n", __func__);
r = 0;
break;
}
buf = fifo8_pop_buf(&s->rx_fifo, word_size, &num);
memcpy(&r, buf, num);
if (fifo8_is_empty(&s->rx_fifo)) {
run = true;
}
break;
}
case R_STATUS: {
int val = 0;
val |= (fifo8_num_used(&s->tx_fifo) << R_STATUS_TXFIFO_SHIFT);
val |= (fifo8_num_used(&s->rx_fifo) << R_STATUS_RXFIFO_SHIFT);
r |= val;
break;
}
default:
break;
}
if (run) {
apple_spi_run(s);
}
apple_spi_update_irq(s);
return r;
}
static void ipod_touch_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
{
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;
uint32_t *mmio = &REG(s, addr);
uint32_t old = *mmio;
bool cs_flg = false;
bool run = false;
switch (addr) {
case R_CTRL:
if (r & R_CTRL_TX_RESET) {
fifo8_reset(&s->tx_fifo);
}
if (r & R_CTRL_RX_RESET) {
fifo8_reset(&s->rx_fifo);
}
if (r & R_CTRL_RUN && !fifo8_is_empty(&s->tx_fifo)) {
run = true;
}
break;
case R_STATUS:
run = true;
r = old & (~r);
break;
case R_PIN:
cs_flg = true;
break;
case R_TXDATA ... R_TXDATA + 3: {
int word_size = apple_spi_word_size(s);
if (fifo8_is_full(&s->tx_fifo) || fifo8_num_free(&s->tx_fifo) < word_size) {
hw_error("Tx buffer overflow: %d\n", fifo8_num_free(&s->tx_fifo));
}
fifo8_push_all(&s->tx_fifo, (uint8_t *)&r, word_size);
break;
case R_CFG:
run = true;
break;
}
default:
break;
}
*mmio = r;
if (cs_flg) {
apple_spi_update_cs(s);
}
if (run) {
apple_spi_run(s);
}
if(addr == R_STATUS) {
apple_spi_update_xfer_tx(s);
apple_spi_update_xfer_rx(s);
}
apple_spi_update_irq(s);
}
static const MemoryRegionOps spi_ops = {
.read = ipod_touch_spi_read,
.write = ipod_touch_spi_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_spi_reset(DeviceState *d)
{
IPodTouchSPIState *s = (IPodTouchSPIState *)d;
memset(s->regs, 0, sizeof(s->regs));
fifo8_reset(&s->tx_fifo);
fifo8_reset(&s->rx_fifo);
}
static uint32_t base_addr = 0;
void set_spi_base(uint32_t base)
{
base_addr = base;
}
static void ipod_touch_spi_realize(DeviceState *dev, struct Error **errp)
{
IPodTouchSPIState *s = IPOD_TOUCH_SPI(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
char bus_name[32] = { 0 };
snprintf(bus_name, sizeof(bus_name), "%s.bus", dev->id);
s->spi = ssi_create_bus(dev, (const char *)bus_name);
sysbus_init_irq(sbd, &s->irq);
sysbus_init_irq(sbd, &s->cs_line);
qdev_init_gpio_in_named(dev, apple_spi_cs_set, SSI_GPIO_CS, 1);
char name[5];
snprintf(name, 5, "spi%d", base_addr);
memory_region_init_io(&s->iomem, OBJECT(s), &spi_ops, s, name, 0x100);
sysbus_init_mmio(sbd, &s->iomem);
s->base = base_addr;
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;
}
}
}
static void ipod_touch_spi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ipod_touch_spi_realize;
dc->reset = ipod_touch_spi_reset;
}
static const TypeInfo ipod_touch_spi_info = {
.name = TYPE_IPOD_TOUCH_SPI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchSPIState),
.class_init = ipod_touch_spi_class_init,
};
static void ipod_touch_spi_register_types(void)
{
type_register_static(&ipod_touch_spi_info);
}
type_init(ipod_touch_spi_register_types)
+129
View File
@@ -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)
+143
View File
@@ -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)
+178
View File
@@ -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)
+56
View File
@@ -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)
+616
View File
@@ -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" TARGET_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" TARGET_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" TARGET_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" TARGET_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)
+86
View File
@@ -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
View File
@@ -62,5 +62,6 @@ arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.
arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c', '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_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'))
hw_arch += {'arm': arm_ss}
+19 -7
View File
@@ -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);
+244
View File
@@ -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)
+1
View File
@@ -16,4 +16,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'))
softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
+3
View File
@@ -8,6 +8,9 @@ config I8259
config PL190
bool
config PL192
bool
config IOAPIC
bool
select I8259
+1
View File
@@ -19,6 +19,7 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c'))
softmmu_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'))
softmmu_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c'))
softmmu_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c'))
softmmu_ss.add(when: 'CONFIG_PL192', if_true: files('pl192.c'))
softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c'))
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c'))
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_intc.c'))
+439
View File
@@ -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) " TARGET_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 = "
TARGET_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) " TARGET_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) " TARGET_FMT_plx "\n", offset);
}
if (offset >= 0xfe0 && offset < 0x1000) {
hw_error("pl192: attempt to write to a read-only register (offset = "
TARGET_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 = "
TARGET_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) " TARGET_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)
+160
View File
@@ -0,0 +1,160 @@
#ifndef HW_ARM_IPOD_TOUCH_H
#define HW_ARM_IPOD_TOUCH_H
#include "exec/hwaddr.h"
#include "hw/boards.h"
#include "hw/intc/pl192.h"
#include "hw/arm/boot.h"
#include "ui/console.h"
#include "cpu.h"
#include "hw/dma/pl080.h"
#include "hw/i2c/ipod_touch_i2c.h"
#include "hw/arm/ipod_touch_timer.h"
#include "hw/arm/ipod_touch_clock.h"
#include "hw/arm/ipod_touch_chipid.h"
#include "hw/arm/ipod_touch_gpio.h"
#include "hw/arm/ipod_touch_sysic.h"
#include "hw/arm/ipod_touch_usb_otg.h"
#include "hw/arm/ipod_touch_usb_phys.h"
#include "hw/arm/ipod_touch_spi.h"
#include "hw/arm/ipod_touch_sha1.h"
#include "hw/arm/ipod_touch_aes.h"
#include "hw/arm/ipod_touch_pke.h"
#include "hw/arm/ipod_touch_unknown1.h"
#include "hw/arm/ipod_touch_lcd.h"
#include "hw/arm/ipod_touch_mipi_dsi.h"
#include "hw/arm/ipod_touch_fmss.h"
#include "hw/arm/ipod_touch_mbx.h"
#include "hw/arm/ipod_touch_scaler_csc.h"
#include "hw/arm/ipod_touch_sdio.h"
#include "hw/arm/ipod_touch_tvout.h"
#define TYPE_IPOD_TOUCH "iPod-Touch"
#define S5L8720_VIC_N 2
#define S5L8720_VIC_SIZE 32
#define S5L8720_TIMER1_IRQ 0x7
#define S5L8720_USB_OTG_IRQ 0x13
#define S5L8720_SPI0_IRQ 0x9
#define S5L8720_SPI1_IRQ 0xA
#define S5L8720_SPI2_IRQ 0xB
#define S5L8720_LCD_IRQ 0xD
#define S5L8720_DMAC0_IRQ 0x10
#define S5L8720_DMAC1_IRQ 0x11
#define S5L8720_I2C0_IRQ 0x15
#define S5L8720_SPI3_IRQ 0x1C
#define S5L8720_I2C1_IRQ 0x16
#define S5L8720_TVOUT_SDO_IRQ 0x1E
#define S5L8720_SDIO_IRQ 0x2A
#define S5L8720_FMSS_IRQ 0x36
#define S5L8720_SPI4_IRQ 0x37
// GPIO interrupts
#define S5L8900_GPIO_G0_IRQ 0x21
#define S5L8900_GPIO_G1_IRQ 0x20
#define S5L8900_GPIO_G2_IRQ 0x1F
#define S5L8900_GPIO_G3_IRQ 0x03
#define S5L8900_GPIO_G4_IRQ 0x02
const int S5L8900_GPIO_IRQS[5] = { S5L8900_GPIO_G0_IRQ, S5L8900_GPIO_G1_IRQ, S5L8900_GPIO_G2_IRQ, S5L8900_GPIO_G3_IRQ, S5L8900_GPIO_G4_IRQ };
#define IT2G_CPREG_VAR_NAME(name) cpreg_##name
#define IT2G_CPREG_VAR_DEF(name) uint64_t IT2G_CPREG_VAR_NAME(name)
#define TYPE_IPOD_TOUCH_MACHINE MACHINE_TYPE_NAME(TYPE_IPOD_TOUCH)
#define IPOD_TOUCH_MACHINE(obj) \
OBJECT_CHECK(IPodTouchMachineState, (obj), TYPE_IPOD_TOUCH_MACHINE)
// memory addresses
#define VROM_MEM_BASE 0x0
#define INSECURE_RAM_MEM_BASE 0x8000000
#define SECURE_RAM_MEM_BASE 0xB000000
#define FRAMEBUFFER_MEM_BASE 0xFB00000
#define IBOOT_MEM_BASE 0xFF00000
#define SRAM1_MEM_BASE 0x22020000
#define SHA1_MEM_BASE 0x38000000
#define DMAC0_MEM_BASE 0x38200000
#define USBOTG_MEM_BASE 0x38400000
#define DMAC1_0_MEM_BASE 0x38700000
#define DISPLAY_MEM_BASE 0x38900000
#define FMSS_MEM_BASE 0x38A00000
#define AES_MEM_BASE 0x38C00000
#define SDIO_MEM_BASE 0x38D00000
#define VIC0_MEM_BASE 0x38E00000
#define VIC1_MEM_BASE 0x38E01000
#define EDGEIC_MEM_BASE 0x38E02000
#define H264_MEM_BASE 0x38F00000
#define SCALER_CSC_MEM_BASE 0x39000000
#define TVOUT_MIXER2_MEM_BASE 0x39100000
#define TVOUT_MIXER1_MEM_BASE 0x39200000
#define TVOUT_SDO_MEM_BASE 0x39300000
#define SYSIC_MEM_BASE 0x39700000
#define DMAC1_1_MEM_BASE 0x39900000
#define MBX1_MEM_BASE 0x3B000000
#define MBX2_MEM_BASE 0x39400000
#define SPI0_MEM_BASE 0x3C300000
#define USBPHYS_MEM_BASE 0x3C400000
#define CLOCK0_MEM_BASE 0x3C500000
#define I2C0_MEM_BASE 0x3C600000
#define TIMER1_MEM_BASE 0x3C700000
#define I2C1_MEM_BASE 0x3C900000
#define UART0_MEM_BASE 0x3CC00000
#define SPI1_MEM_BASE 0x3CE00000
#define GPIO_MEM_BASE 0x3CF00000
#define PKE_MEM_BASE 0x3D000000
#define CHIPID_MEM_BASE 0x3D100000
#define SPI2_MEM_BASE 0x3D200000
#define UNKNOWN1_MEM_BASE 0x3D700000
#define MIPI_DSI_MEM_BASE 0x3D800000
#define SPI3_MEM_BASE 0x3DA00000
#define UART1_MEM_BASE 0x3DB00000
#define UART2_MEM_BASE 0x3DC00000
#define UART3_MEM_BASE 0x3DD00000
#define SWI_MEM_BASE 0x3DE00000
#define CLOCK1_MEM_BASE 0x3E000000
#define SPI4_MEM_BASE 0x3E100000
typedef struct {
MachineClass parent;
} IPodTouchMachineClass;
typedef struct {
MachineState parent;
AddressSpace *nsas;
qemu_irq **irq;
ARMCPU *cpu;
PL192State *vic0;
PL192State *vic1;
IPodTouchClockState *clock0;
IPodTouchClockState *clock1;
IPodTouchTimerState *timer1;
IPodTouchSPIState *spi0_state;
IPodTouchSPIState *spi1_state;
IPodTouchSPIState *spi4_state;
IPodTouchChipIDState *chipid_state;
IPodTouchGPIOState *gpio_state;
IPodTouchSYSICState *sysic;
synopsys_usb_state *usb_otg;
IPodTouchUSBPhysState *usb_phys_state;
IPodTouchSHA1State *sha1_state;
IPodTouchAESState *aes_state;
IPodTouchPKEState *pke_state;
IPodTouchI2CState *i2c0_state;
IPodTouchI2CState *i2c1_state;
IPodTouchLCDState *lcd_state;
IPodTouchMIPIDSIState *mipi_dsi_state;
IPodTouchFMSSState *fmss_state;
IPodTouchMBXState *mbx_state;
IPodTouchScalerCSCState *scaler_csc_state;
IPodTouchSDIOState *sdio_state;
IPodTouchTVOutState *tvout_state;
Clock *sysclk;
char bootrom_path[1024];
char nor_path[1024];
char nand_path[1024];
IT2G_CPREG_VAR_DEF(REG0);
IT2G_CPREG_VAR_DEF(REG1);
} IPodTouchMachineState;
#endif
+70
View File
@@ -0,0 +1,70 @@
#ifndef HW_ARM_IPOD_TOUCH_AES_H
#define HW_ARM_IPOD_TOUCH_AES_H
#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "exec/hwaddr.h"
#include "exec/memory.h"
#include <openssl/aes.h>
#define TYPE_IPOD_TOUCH_AES "ipodtouch.aes"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchAESState, IPOD_TOUCH_AES)
#define key_uid ((uint8_t[]){0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF})
#define AES_128_CBC_BLOCK_SIZE 64
#define AES_CONTROL 0x0
#define AES_GO 0x4
#define AES_UNKREG0 0x8
#define AES_STATUS 0xC
#define AES_UNKREG1 0x10
#define AES_KEYLEN 0x14
#define AES_INSIZE 0x18
#define AES_INADDR 0x20
#define AES_OUTSIZE 0x24
#define AES_OUTADDR 0x28
#define AES_AUXSIZE 0x2C
#define AES_AUXADDR 0x30
#define AES_SIZE3 0x34
#define AES_KEY_REG 0x4C
#define AES_TYPE 0x6C
#define AES_IV_REG 0x74
#define AES_KEYSIZE 0x20
#define AES_IVSIZE 0x10
typedef enum AESKeyType {
AESCustom = 0,
AESGID = 1,
AESUID = 2
} AESKeyType;
typedef enum AESKeyLen {
AES128 = 0,
AES192 = 1,
AES256 = 2
} AESKeyLen;
typedef struct IPodTouchAESState
{
SysBusDevice busdev;
MemoryRegion iomem;
AES_KEY decryptKey;
uint32_t ivec[4];
uint32_t insize;
uint32_t inaddr;
uint32_t outsize;
uint32_t outaddr;
uint32_t auxaddr;
uint32_t keytype;
uint32_t status;
uint32_t ctrl;
uint32_t unkreg0;
uint32_t unkreg1;
uint32_t operation;
uint32_t keylen;
uint32_t custkey[8];
uint8_t gid_encryption_count;
} IPodTouchAESState;
#endif
+20
View File
@@ -0,0 +1,20 @@
#ifndef HW_CD3272_MIKEY_H
#define HW_CD3272_MIKEY_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#include "time.h"
#define TYPE_CD3272MIKEY "cd3272mikey"
OBJECT_DECLARE_SIMPLE_TYPE(CD3272MikeyState, CD3272MIKEY)
typedef struct CD3272MikeyState {
I2CSlave i2c;
uint32_t cmd;
} CD3272MikeyState;
#endif
+21
View File
@@ -0,0 +1,21 @@
#ifndef HW_ARM_IPOD_TOUCH_CHIPID_H
#define HW_ARM_IPOD_TOUCH_CHIPID_H
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_CHIPID "ipodtouch.chipid"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchChipIDState, IPOD_TOUCH_CHIPID)
#define CHIPID_UNKNOWN1 0x4
#define CHIPID_INFO 0x8
#define CHIPID_UNKNOWN2 0xC
#define CHIPID_UNKNOWN3 0x10
typedef struct IPodTouchChipIDState {
SysBusDevice busdev;
MemoryRegion iomem;
} IPodTouchChipIDState;
#endif
+68
View File
@@ -0,0 +1,68 @@
#ifndef IPOD_TOUCH_CLOCK_H
#define IPOD_TOUCH_CLOCK_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/clock.h"
#define TYPE_IPOD_TOUCH_CLOCK "ipodtouch.clock"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchClockState, IPOD_TOUCH_CLOCK)
#define CLOCK_CONFIG0 0x0
#define CLOCK_CONFIG1 0x4
#define CLOCK_CONFIG2 0x8
#define CLOCK_CONFIG3 0xC
#define CLOCK_CONFIG4 0x10
#define CLOCK_CONFIG5 0x14
#define CLOCK_PLL0CON 0x20
#define CLOCK_PLL1CON 0x24
#define CLOCK_PLL2CON 0x28
#define CLOCK_PLL3CON 0x2C
#define CLOCK_PLL0LCNT 0x30
#define CLOCK_PLL1LCNT 0x34
#define CLOCK_PLL2LCNT 0x38
#define CLOCK_PLL3LCNT 0x3C
#define CLOCK_PLLLOCK 0x40
#define CLOCK_PLLMODE 0x44
#define CLOCK_PWRCON0 0x48
#define CLOCK_PWRCON1 0x4C
#define CLOCK_PWRCON2 0x58
#define CLOCK_PWRCON3 0x68
#define CLOCK_PWRCON4 0x6C
typedef struct IPodTouchClockState
{
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t config0;
uint32_t config1;
uint32_t config2;
uint32_t config3;
uint32_t config4;
uint32_t config5;
uint32_t pll0con;
uint32_t pll1con;
uint32_t pll2con;
uint32_t pll3con;
uint32_t pll0lcnt;
uint32_t pll1lcnt;
uint32_t pll2lcnt;
uint32_t pll3lcnt;
uint32_t pllmode;
uint32_t pwrcon0;
uint32_t pwrcon1;
uint32_t pwrcon2;
uint32_t pwrcon3;
uint32_t pwrcon4;
} IPodTouchClockState;
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef IPOD_TOUCH_FMSS_H
#define IPOD_TOUCH_FMSS_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_FMSS "ipodtouch.fmss"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchFMSSState, IPOD_TOUCH_FMSS)
#define NAND_BYTES_PER_PAGE 4096
#define NAND_BYTES_PER_SPARE 64
#define FMSS__FMCTRL1 0x4
#define FMSS__CS_IRQ 0xC0C
#define FMSS__CS_IRQMASK 0xC10
#define FMSS__CS_BUF_RST_OK 0xC64
#define FMSS_CINFO_TARGET_ADDR 0xD08
#define FMSS_PAGES_IN_ADDR 0xD0C
#define FMSS_CS_BUF_ADDR 0xD10
#define FMSS_NUM_PAGES 0xD18
#define FMSS_PAGE_SPARE_OUT_ADDR 0xD1C
#define FMSS_PAGES_OUT_ADDR 0xD20
#define FMSS_CSGENRC 0xD30
typedef struct IPodTouchFMSSState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint8_t *page_buffer;
uint8_t *page_spare_buffer;
uint32_t reg_cs_irq_bit;
uint32_t reg_cinfo_target_addr;
uint32_t reg_pages_in_addr;
uint32_t reg_cs_buf_addr;
uint32_t reg_num_pages;
uint32_t reg_page_spare_out_addr;
uint32_t reg_pages_out_addr;
uint32_t reg_csgenrc;
char *nand_path;
} IPodTouchFMSSState;
#endif
+28
View File
@@ -0,0 +1,28 @@
#ifndef IPOD_TOUCH_GPIO_H
#define IPOD_TOUCH_GPIO_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_GPIO "ipodtouch.gpio"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchGPIOState, IPOD_TOUCH_GPIO)
#define GPIO_BUTTON_POWER 0xC02
#define GPIO_BUTTON_HOME 0xC01
#define GPIO_BUTTON_POWER_IRQ 0x7A
#define GPIO_BUTTON_HOME_IRQ 0x79
#define NUM_GPIO_PINS 0x20
typedef struct IPodTouchGPIOState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t gpio_state;
} IPodTouchGPIOState;
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef IPOD_TOUCH_LCD_H
#define IPOD_TOUCH_LCD_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/arm/ipod_touch_multitouch.h"
#define TYPE_IPOD_TOUCH_LCD "ipodtouch.lcd"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchLCDState, IPOD_TOUCH_LCD)
#define LCD_REFRESH_RATE_FREQUENCY 10
typedef struct IPodTouchLCDState
{
SysBusDevice parent_obj;
MemoryRegion *sysmem;
MemoryRegion iomem;
QemuConsole *con;
IPodTouchMultitouchState *mt;
int invalidate;
MemoryRegionSection fbsection;
qemu_irq irq;
uint32_t lcd_con;
uint32_t w1_display_resolution_info;
uint32_t w1_framebuffer_base;
uint32_t w1_hspan;
uint32_t w1_display_depth_info;
uint32_t unknown1;
QEMUTimer *refresh_timer;
} IPodTouchLCDState;
#endif
+23
View File
@@ -0,0 +1,23 @@
#ifndef HW_LIS302DL_H
#define HW_LIS302DL_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#define TYPE_LIS302DL "lis302dl"
OBJECT_DECLARE_SIMPLE_TYPE(LIS302DLState, LIS302DL)
#define ACCEL_WHOAMI 0x0F
#define ACCEL_WHOAMI_VALUE 0x3B
typedef struct LIS302DLState {
I2CSlave i2c;
uint32_t cmd;
} LIS302DLState;
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef HW_ARM_IPOD_TOUCH_MBX_H
#define HW_ARM_IPOD_TOUCH_MBX_H
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_MBX "ipodtouch.mbx"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMBXState, IPOD_TOUCH_MBX)
typedef struct IPodTouchMBXState {
SysBusDevice busdev;
MemoryRegion iomem1;
MemoryRegion iomem2;
} IPodTouchMBXState;
#endif
+33
View File
@@ -0,0 +1,33 @@
#ifndef IPOD_TOUCH_MIPI_DSI_H
#define IPOD_TOUCH_MIPI_DSI_H
#include <math.h>
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_MIPI_DSI "ipodtouch.mipidsi"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMIPIDSIState, IPOD_TOUCH_MIPI_DSI)
#define REG_INTSRC 0x2C
#define REG_PKTHDR 0x34
#define REG_RXFIFO 0x3C
#define REG_FIFOCTRL 0x44
#define DSIM_RSP_LONG_READ 0x1A
#define rDSIM_FIFOCTRL_EmptyHSfr 0x400000
#define rDSIM_STATUS_TxReadyHsClk 0x400
#define rDSIM_INTSRC_RxDatDone 0x00040000
typedef struct IPodTouchMIPIDSIState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t pkthdr_reg;
bool return_panel_id;
} IPodTouchMIPIDSIState;
#endif
+160
View File
@@ -0,0 +1,160 @@
#ifndef IPOD_TOUCH_MULTITOUCH_H
#define IPOD_TOUCH_MULTITOUCH_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/ssi/ssi.h"
#include "hw/arm/ipod_touch_sysic.h"
#include "hw/arm/ipod_touch_gpio.h"
#define TYPE_IPOD_TOUCH_MULTITOUCH "ipodtouch.multitouch"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMultitouchState, IPOD_TOUCH_MULTITOUCH)
#define MT_INTERFACE_VERSION 0x1
#define MT_FAMILY_ID 81
#define MT_ENDIANNESS 0x1
#define MT_SENSOR_ROWS 15
#define MT_SENSOR_COLUMNS 10
#define MT_BCD_VERSION 51
#define MT_SENSOR_REGION_DESC 0x0
#define MT_SENSOR_REGION_PARAM 0x0
#define MT_MAX_PACKET_SIZE 0x294 // 660
#define MT_SENSOR_SURFACE_WIDTH 5000
#define MT_SENSOR_SURFACE_HEIGHT 7500
// internal surface width/height
#define MT_INTERNAL_SENSOR_SURFACE_WIDTH (9000 - MT_SENSOR_SURFACE_WIDTH) * 84 / 73
#define MT_INTERNAL_SENSOR_SURFACE_HEIGHT (13850 - MT_SENSOR_SURFACE_HEIGHT) * 84 / 73
// report IDs
#define MT_REPORT_UNKNOWN1 0x70
#define MT_REPORT_FAMILY_ID 0xD1
#define MT_REPORT_SENSOR_INFO 0xD3
#define MT_REPORT_SENSOR_REGION_DESC 0xD0
#define MT_REPORT_SENSOR_REGION_PARAM 0xA1
#define MT_REPORT_SENSOR_DIMENSIONS 0xD9
// report sizes
#define MT_REPORT_UNKNOWN1_SIZE 0x1
#define MT_REPORT_FAMILY_ID_SIZE 0x1
#define MT_REPORT_SENSOR_INFO_SIZE 0x5
#define MT_REPORT_SENSOR_REGION_DESC_SIZE 0x1
#define MT_REPORT_SENSOR_REGION_PARAM_SIZE 0x1
#define MT_REPORT_SENSOR_DIMENSIONS_SIZE 0x8
#define MT_CMD_HBPP_DATA_PACKET 0x30
#define MT_CMD_GET_CMD_STATUS 0xE1
#define MT_CMD_GET_INTERFACE_VERSION 0xE2
#define MT_CMD_GET_REPORT_INFO 0xE3
#define MT_CMD_SHORT_CONTROL_WRITE 0xE4
#define MT_CMD_SHORT_CONTROL_READ 0xE6
#define MT_CMD_FRAME_READ 0xEA
// frame types
#define MT_FRAME_TYPE_PATH 0x44
// frame event types
#define MT_EVENT_TOUCH_FULL_END 0x0
#define MT_EVENT_TOUCH_START 0x3
#define MT_EVENT_TOUCH_MOVED 0x4
#define MT_EVENT_TOUCH_ENDED 0x7
typedef struct MTFrameLengthPacket
{
uint8_t cmd;
uint8_t length1;
uint8_t length2;
uint8_t unused[11];
uint8_t checksum1;
uint8_t checksum2;
} __attribute__((__packed__)) MTFrameLengthPacket;
typedef struct MTFrameHeader
{
uint8_t type;
uint8_t frameNum;
uint8_t headerLen;
uint8_t unk_3;
uint32_t timestamp;
uint8_t unk_8;
uint8_t unk_9;
uint8_t unk_A;
uint8_t unk_B;
uint16_t unk_C;
uint16_t isImage;
uint8_t numFingers;
uint8_t fingerDataLen;
uint16_t unk_12;
uint16_t unk_14;
uint16_t unk_16;
} __attribute__((__packed__)) MTFrameHeader;
typedef struct MTFramePacket
{
uint8_t cmd;
uint8_t unused1;
uint8_t length1;
uint8_t length2;
uint8_t checksum_pad;
MTFrameHeader header;
} __attribute__((__packed__)) MTFramePacket;
typedef struct FingerData
{
uint8_t id;
uint8_t event;
uint8_t unk_2;
uint8_t unk_3;
int16_t x;
int16_t y;
int16_t velX;
int16_t velY;
uint16_t radius2;
uint16_t radius3;
uint16_t angle;
uint16_t radius1;
uint16_t contactDensity;
uint16_t unk_16;
uint16_t unk_18;
uint16_t unk_1A;
} __attribute__((__packed__)) FingerData;
typedef struct MTFrame {
MTFrameLengthPacket frame_length;
MTFramePacket frame_packet;
FingerData finger_data; // TODO we assume one finger for now
uint8_t checksum1;
uint8_t checksum2;
} __attribute__((__packed__)) MTFrame;
typedef struct IPodTouchMultitouchState {
SSIPeripheral ssidev;
uint8_t cur_cmd;
uint8_t *out_buffer;
uint8_t *in_buffer;
uint32_t buf_size;
uint32_t buf_ind;
uint32_t in_buffer_ind;
uint8_t hbpp_atn_ack_response[2];
MTFrame *next_frame;
uint32_t frame_counter;
bool touch_down;
QEMUTimer *touch_timer;
QEMUTimer *touch_end_timer;
IPodTouchSYSICState *sysic;
IPodTouchGPIOState *gpio_state;
float touch_x;
float touch_y;
float prev_touch_x;
float prev_touch_y;
uint64_t last_frame_timestamp;
} IPodTouchMultitouchState;
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s);
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s);
#endif
+38
View File
@@ -0,0 +1,38 @@
#ifndef IPOD_TOUCH_NOR_SPI_H
#define IPOD_TOUCH_NOR_SPI_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/ssi/ssi.h"
#include "hw/hw.h"
#define TYPE_IPOD_TOUCH_NOR_SPI "ipodtouch.norspi"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchNORSPIState, IPOD_TOUCH_NOR_SPI)
#define NOR_WRITE_TO_STATUS_REG 0x1
#define NOR_WRITE_DATA_CMD 0x2
#define NOR_READ_DATA_CMD 0x3
#define NOR_DISABLE_WRITE 0x4
#define NOR_GET_STATUS_CMD 0x5
#define NOR_ENABLE_WRITE 0x6
#define NOR_ERASE_BLOCK 0x20
#define NOR_GET_JEDECID 0x9F
typedef struct IPodTouchNORSPIState {
SSIPeripheral ssidev;
char *nor_path;
uint32_t cur_cmd;
uint8_t *in_buf;
uint8_t *out_buf;
uint32_t in_buf_size;
uint32_t out_buf_size;
uint32_t in_buf_cur_ind;
uint32_t out_buf_cur_ind;
uint8_t *nor_data;
uint8_t write_enabled;
uint32_t nor_read_ind;
bool nor_initialized;
} IPodTouchNORSPIState;
#endif
+32
View File
@@ -0,0 +1,32 @@
#ifndef HW_PCF50633_PMU_H
#define HW_PCF50633_PMU_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#include "time.h"
#define TYPE_PCF50633 "pcf50633"
OBJECT_DECLARE_SIMPLE_TYPE(Pcf50633State, PCF50633)
#define PMU_MBCS1 0x4B
#define PMU_ADCC1 0x57
// RTC registers
#define PMU_RTCSC 0x59
#define PMU_RTCMN 0x5A
#define PMU_RTCHR 0x5B
#define PMU_RTCWD 0x5C
#define PMU_RTCDT 0x5D
#define PMU_RTCMT 0x5E
#define PMU_RTCYR 0x5F
typedef struct Pcf50633State {
I2CSlave i2c;
uint32_t cmd;
} Pcf50633State;
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef HW_ARM_IPOD_TOUCH_PKE_H
#define HW_ARM_IPOD_TOUCH_PKE_H
#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "exec/hwaddr.h"
#include "exec/memory.h"
#define TYPE_IPOD_TOUCH_PKE "ipodtouch.pke"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchPKEState, IPOD_TOUCH_PKE)
#define REG_PKE_START 0x8
#define REG_PKE_SEG_SIZE 0x14
#define REG_PKE_SWRESET 0x24
#define REG_PKE_SEG_START 0x800
typedef struct IPodTouchPKEState {
SysBusDevice busdev;
MemoryRegion iomem;
uint8_t segments[1024];
uint32_t seg_size_reg;
uint32_t segment_size;
uint8_t num_started;
} IPodTouchPKEState;
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef HW_ARM_IPOD_TOUCH_SCALER_CSC_H
#define HW_ARM_IPOD_TOUCH_SCALER_CSC_H
#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "exec/hwaddr.h"
#include "exec/memory.h"
#define TYPE_IPOD_TOUCH_SCALER_CSC "ipodtouch.scalercsc"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchScalerCSCState, IPOD_TOUCH_SCALER_CSC)
typedef struct IPodTouchScalerCSCState {
SysBusDevice busdev;
MemoryRegion iomem;
} IPodTouchScalerCSCState;
#endif
+71
View File
@@ -0,0 +1,71 @@
#ifndef IPOD_TOUCH_SDIO_H
#define IPOD_TOUCH_SDIO_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_SDIO "ipodtouch.sdio"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSDIOState, IPOD_TOUCH_SDIO)
#define SDIO_CMD 0x8
#define SDIO_ARGU 0xC
#define SDIO_STATE 0x10
#define SDIO_STAC 0x14
#define SDIO_DSTA 0x18
#define SDIO_RESP0 0x20
#define SDIO_RESP1 0x24
#define SDIO_RESP2 0x28
#define SDIO_RESP3 0x2C
#define SDIO_CSR 0x34
#define SDIO_IRQ 0x38
#define SDIO_IRQMASK 0x3C
#define SDIO_BADDR 0x44
#define SDIO_BLKLEN 0x48
#define SDIO_NUMBLK 0x4C
#define CMD5_FUNC_OFFSET 28
#define CIS_OFFSET 0xC8
#define CIS_MANUFACTURER_ID 0x20
#define CIS_FUNCTION_EXTENSION 0x22
#define BCM4325_FUNCTIONS 0x1
#define BCM4325_MANUFACTURER 0x4D50
#define BCM4325_PRODUCT_ID 0x4D48
typedef struct BCM4325FrameHeaderPacket
{
uint16_t frame_length;
uint16_t checksum;
} __attribute__((__packed__)) BCM4325FrameHeaderPacket;
typedef struct IPodTouchSDIOState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t cmd;
uint32_t arg;
uint32_t state;
uint32_t stac;
uint32_t csr;
uint32_t resp0;
uint32_t resp1;
uint32_t resp2;
uint32_t resp3;
uint32_t irq_reg;
uint32_t irq_mask;
uint32_t baddr;
uint32_t blklen;
uint32_t numblk;
QEMUTimer *irq_timer;
qemu_irq irq;
qemu_irq irq2;
GQueue *rx_fifo;
uint8_t registers[0x10000];
} IPodTouchSDIOState;
#endif
+38
View File
@@ -0,0 +1,38 @@
#ifndef HW_ARM_IPOD_TOUCH_SHA1_H
#define HW_ARM_IPOD_TOUCH_SHA1_H
#include "qemu/osdep.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "exec/hwaddr.h"
#include "exec/memory.h"
#include <openssl/sha.h>
#define TYPE_IPOD_TOUCH_SHA1 "ipodtouch.sha1"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSHA1State, IPOD_TOUCH_SHA1)
#define SHA1_BUFFER_SIZE 1024 * 1024 * 5
#define SHA_CONFIG 0x0
#define SHA_RESET 0x4
#define SHA_MEMORY_MODE 0x80 // whether we read from the memory
#define SHA_MEMORY_START 0x84
#define SHA_INSIZE 0x8c
typedef struct IPodTouchSHA1State {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t config;
uint32_t memory_start;
uint32_t memory_mode;
uint32_t insize;
uint8_t buffer[SHA1_BUFFER_SIZE];
uint32_t hw_buffer[0x10]; // hardware buffer
uint32_t buffer_ind;
uint8_t hashout[0x14];
bool hw_buffer_dirty;
bool hash_computed;
SHA_CTX ctx;
} IPodTouchSHA1State;
#endif
+80
View File
@@ -0,0 +1,80 @@
#ifndef HW_ARM_IPOD_TOUCH_SPI_H
#define HW_ARM_IPOD_TOUCH_SPI_H
#include "qemu/osdep.h"
#include "qemu/fifo8.h"
#include "qemu/log.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/ssi/ssi.h"
#include "hw/arm/ipod_touch_nor_spi.h"
#include "hw/arm/ipod_touch_multitouch.h"
#define TYPE_IPOD_TOUCH_SPI "ipodtouch.spi"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSPIState, IPOD_TOUCH_SPI)
#define R_CTRL 0x000
#define R_CTRL_RUN (1 << 0)
#define R_CTRL_TX_RESET (1 << 2)
#define R_CTRL_RX_RESET (1 << 3)
#define R_CFG 0x004
#define R_CFG_AGD (1 << 0)
#define R_CFG_CPHA (1 << 1)
#define R_CFG_CPOL (1 << 2)
#define R_CFG_IE_RXREADY (1 << 7)
#define R_CFG_IE_TXEMPTY (1 << 8)
#define R_CFG_WORD_SIZE(_x) (((_x) >> 15) & 0x3)
#define R_CFG_WORD_SIZE_8B 0
#define R_CFG_WORD_SIZE_16B 1
#define R_CFG_WORD_SIZE_32B 2
#define R_CFG_IE_COMPLETE (1 << 21)
#define R_STATUS 0x008
#define R_STATUS_RXREADY (1 << 0)
#define R_STATUS_TXEMPTY (1 << 1)
#define R_STATUS_RXOVERFLOW (1 << 3)
#define R_STATUS_COMPLETE (1 << 22)
#define R_STATUS_TXFIFO_SHIFT (6)
#define R_STATUS_RXFIFO_SHIFT (11)
#define R_PIN 0x00c
#define R_PIN_CS (1 << 1)
#define R_TXDATA 0x010
#define R_RXDATA 0x020
#define R_CLKDIV 0x030
#define R_CLKDIV_MAX 0x7ff
#define R_RXCNT 0x034
#define R_WORD_DELAY 0x038
#define R_TXCNT 0x04c
#define R_FIFO_TX_DEPTH 50000
#define R_FIFO_RX_DEPTH 16
#define REG(_s,_v) ((_s)->regs[(_v)>>2])
#define MMIO_SIZE (0x4000)
typedef struct IPodTouchSPIState {
SysBusDevice parent_obj;
MemoryRegion iomem;
SSIBus *spi;
IPodTouchMultitouchState *mt;
IPodTouchNORSPIState *nor;
qemu_irq irq;
uint32_t last_irq;
qemu_irq cs_line;
uint32_t regs[MMIO_SIZE >> 2];
uint8_t base;
Fifo8 rx_fifo;
Fifo8 tx_fifo;
} IPodTouchSPIState;
void set_spi_base(uint32_t base);
#endif
+43
View File
@@ -0,0 +1,43 @@
#ifndef IPOD_TOUCH_SYSIC_H
#define IPOD_TOUCH_SYSIC_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_SYSIC "ipodtouch.sysic"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSYSICState, IPOD_TOUCH_SYSIC)
#define POWER_ID 0x44
#define POWER_ONCTRL 0xC
#define POWER_OFFCTRL 0x10
#define POWER_SETSTATE 0x8
#define POWER_STATE 0x14 // seems to be toggled by writing a 1 to the right device ID - cleared to 0 when the device has started.
#define POWER_ID_ADM 0x10
// the GPIO IC is part of the system controller
#define GPIO_INTLEVEL 0x80
#define GPIO_INTSTAT 0xA0
#define GPIO_INTEN 0xC0
#define GPIO_INTTYPE 0xE0
#define GPIO_NUMINTGROUPS 7
typedef struct IPodTouchSYSICState {
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq gpio_irqs[GPIO_NUMINTGROUPS];
uint32_t power_id;
uint32_t power_state;
// GPIO
uint32_t gpio_int_level[GPIO_NUMINTGROUPS];
uint32_t gpio_int_status[GPIO_NUMINTGROUPS];
uint32_t gpio_int_enabled[GPIO_NUMINTGROUPS];
uint32_t gpio_int_type[GPIO_NUMINTGROUPS];
} IPodTouchSYSICState;
#endif
+52
View File
@@ -0,0 +1,52 @@
#ifndef IPOD_TOUCH_TIMER_H
#define IPOD_TOUCH_TIMER_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/clock.h"
#define TYPE_IPOD_TOUCH_TIMER "ipodtouch.timer"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTimerState, IPOD_TOUCH_TIMER)
#define TIMER_IRQSTAT 0x10000
#define TIMER_IRQLATCH 0x118
#define TIMER_TICKSHIGH 0x80
#define TIMER_TICKSLOW 0x84
#define TIMER_STATE_START 1
#define TIMER_STATE_STOP 0
#define TIMER_STATE_MANUALUPDATE 2
#define NUM_TIMERS 7
#define TIMER_4 0xA0
#define TIMER_CONFIG 0
#define TIMER_STATE 0x4
#define TIMER_COUNT_BUFFER 0x8
#define TIMER_COUNT_BUFFER2 0xC
typedef struct IPodTouchTimerState
{
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t ticks_high;
uint32_t ticks_low;
uint32_t status;
uint32_t config;
uint32_t bcount1;
uint32_t bcount2;
uint32_t prescaler;
uint32_t irqstat;
QEMUTimer *st_timer;
Clock *sysclk;
uint32_t bcreload;
uint32_t freq_out;
uint64_t tick_interval;
uint64_t last_tick;
uint64_t next_planned_tick;
uint64_t base_time;
qemu_irq irq;
} IPodTouchTimerState;
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef IPOD_TOUCH_TVOUT_H
#define IPOD_TOUCH_TVOUT_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#define SDO_CLKCON 0x0
#define SDO_CONFIG 0x8
#define SDO_IRQ 0x280
#define SDO_IRQMASK 0x284
#define MXR_STATUS 0x0
#define MXR_CFG 0x4
#define TYPE_IPOD_TOUCH_TVOUT "ipodtouch.tvout"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTVOutState, IPOD_TOUCH_TVOUT)
typedef struct IPodTouchTVOutState {
SysBusDevice parent_obj;
MemoryRegion mixer1_iomem;
MemoryRegion mixer2_iomem;
MemoryRegion sdo_iomem;
qemu_irq irq;
uint32_t mixer1_status;
uint32_t mixer1_cfg;
uint32_t mixer2_status;
uint32_t mixer2_cfg;
uint32_t sdo_clkcon;
uint32_t sdo_config;
uint32_t sdo_irq;
uint32_t sdo_irq_mask;
uint32_t irq_count;
} IPodTouchTVOutState;
#endif
+16
View File
@@ -0,0 +1,16 @@
#ifndef HW_ARM_IPOD_TOUCH_UNKNOWN1_H
#define HW_ARM_IPOD_TOUCH_UNKNOWN1_H
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_UNKNOWN1 "ipodtouch.unknown1"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchUnknown1State, IPOD_TOUCH_UNKNOWN1)
typedef struct IPodTouchUnknown1State {
SysBusDevice busdev;
MemoryRegion iomem;
} IPodTouchUnknown1State;
#endif
+267
View File
@@ -0,0 +1,267 @@
#ifndef HW_ARM_IPOD_TOUCH_USB_OTG_H
#define HW_ARM_IPOD_TOUCH_USB_OTG_H
#include "hw/irq.h"
#include "hw/usb.h"
#define DEVICE_NAME "usb_synopsys"
#define TYPE_S5L8900USBOTG "s5l8900usbotg"
OBJECT_DECLARE_SIMPLE_TYPE(synopsys_usb_state, S5L8900USBOTG)
// Maximums supported by OIB
#define USB_NUM_ENDPOINTS 8
#define USB_NUM_FIFOS 16
#define RX_FIFO_DEPTH 0x1C0
#define TX_FIFO_DEPTH 0x1C0
#define TX_FIFO_STARTADDR 0x200
#define PERIODIC_TX_FIFO_STARTADDR 0x21B
#define PERIODIC_TX_FIFO_DEPTH 0x100
// Registers
#define GOTGCTL 0x0
#define GOTGINT 0x4
#define GAHBCFG 0x8
#define GUSBCFG 0xC
#define GRSTCTL 0x10
#define GINTSTS 0x14
#define GINTMSK 0x18
#define GRXSTSR 0x1C
#define GRXSTSP 0x20
#define GRXFSIZ 0x24
#define GNPTXFSIZ 0x28
#define GNPTXFSTS 0x2C
#define GHWCFG1 0x44
#define GHWCFG2 0x48
#define GHWCFG3 0x4C
#define GHWCFG4 0x50
#define DIEPTXF(x) (0x100 + (4*(x)))
#define DCFG 0x800
#define DCTL 0x804
#define DSTS 0x808
#define DIEPMSK 0x810
#define DOEPMSK 0x814
#define DAINTSTS 0x818
#define DAINTMSK 0x81C
#define DTKNQR1 0x820
#define DTKNQR2 0x824
#define DTKNQR3 0x830
#define DTKNQR4 0x834
#define USB_INREGS 0x900
#define USB_OUTREGS 0xB00
#define USB_EPREGS_SIZE 0x200
#define USB_FIFO_START 0x1000
#define USB_FIFO_SIZE (0x100*(USB_NUM_FIFOS+1))
#define USB_FIFO_END (USB_FIFO_START+USB_FIFO_SIZE)
#define PCGCCTL 0xE00
#define PCGCCTL_ONOFF_MASK 3 // bits 0, 1
#define PCGCCTL_ON 0
#define PCGCCTL_OFF 1
#define GOTGCTL_BSESSIONVALID (1 << 19)
#define GOTGCTL_SESSIONREQUEST (1 << 1)
#define GAHBCFG_DMAEN (1 << 5)
#define GAHBCFG_BSTLEN_SINGLE (0 << 1)
#define GAHBCFG_BSTLEN_INCR (1 << 1)
#define GAHBCFG_BSTLEN_INCR4 (3 << 1)
#define GAHBCFG_BSTLEN_INCR8 (5 << 1)
#define GAHBCFG_BSTLEN_INCR16 (7 << 1)
#define GAHBCFG_MASKINT 0x1
#define GUSBCFG_TURNAROUND_MASK 0xF
#define GUSBCFG_TURNAROUND_SHIFT 10
#define GUSBCFG_HNPENABLE (1 << 9)
#define GUSBCFG_SRPENABLE (1 << 8)
#define GUSBCFG_PHYIF16BIT (1 << 3)
#define USB_UNKNOWNREG1_START 0x1708
#define GHWCFG2_TKNDEPTH_SHIFT 26
#define GHWCFG2_TKNDEPTH_MASK 0xF
#define GHWCFG2_NUM_ENDPOINTS_SHIFT 10
#define GHWCFG2_NUM_ENDPOINTS_MASK 0xf
#define GHWCFG4_DED_FIFO_EN (1 << 25)
#define GRSTCTL_AHBIDLE (1 << 31)
#define GRSTCTL_TXFFLUSH (1 << 5)
#define GRSTCTL_TXFFNUM_SHIFT 6
#define GRSTCTL_TXFFNUM_MASK 0x1f
#define GRSTCTL_CORESOFTRESET 0x1
#define GRSTCTL_TKNFLUSH 3
#define GINTMSK_NONE 0x0
#define GINTMSK_OTG (1 << 2)
#define GINTMSK_SOF (1 << 3)
#define GINTMSK_GINNAKEFF (1 << 6)
#define GINTMSK_GOUTNAKEFF (1 << 7)
#define GINTMSK_SUSPEND (1 << 11)
#define GINTMSK_RESET (1 << 12)
#define GINTMSK_ENUMDONE (1 << 13)
#define GINTMSK_EPMIS (1 << 17)
#define GINTMSK_INEP (1 << 18)
#define GINTMSK_OEP (1 << 19)
#define GINTMSK_DISCONNECT (1 << 29)
#define GINTMSK_RESUME (1 << 31)
#define GOTGINT_SESENDDET (1 << 2)
#define FIFO_DEPTH_SHIFT 16
#define GNPTXFSTS_GET_TXQSPCAVAIL(x) GET_BITS(x, 16, 8)
#define GHWCFG4_DED_FIFO_EN (1 << 25)
#define DAINT_ALL 0xFFFFFFFF
#define DAINT_NONE 0
#define DAINT_OUT_SHIFT 16
#define DAINT_IN_SHIFT 0
#define DCTL_SFTDISCONNECT 0x2
#define DCTL_PROGRAMDONE (1 << 11)
#define DCTL_CGOUTNAK (1 << 10)
#define DCTL_SGOUTNAK (1 << 9)
#define DCTL_CGNPINNAK (1 << 8)
#define DCTL_SGNPINNAK (1 << 7)
#define DSTS_GET_SPEED(x) GET_BITS(x, 1, 2)
#define DCFG_NZSTSOUTHSHK (1 << 2)
#define DCFG_EPMSCNT (1 << 18)
#define DCFG_HISPEED 0x0
#define DCFG_FULLSPEED 0x1
#define DCFG_DEVICEADDR_UNSHIFTED_MASK 0x7F
#define DCFG_DEVICEADDR_SHIFT 4
#define DCFG_DEVICEADDRMSK (DCFG_DEVICEADDR_UNSHIFTED_MASK << DCFG_DEVICEADDR_SHIFT)
#define DCFG_ACTIVE_EP_COUNT_MASK 0x1f
#define DCFG_ACTIVE_EP_COUNT_SHIFT 18
#define DOEPTSIZ0_SUPCNT_MASK 0x3
#define DOEPTSIZ0_SUPCNT_SHIFT 29
#define DOEPTSIZ0_PKTCNT_MASK 0x1
#define DEPTSIZ0_XFERSIZ_MASK 0x7F
#define DIEPTSIZ_MC_MASK 0x3
#define DIEPTSIZ_MC_SHIFT 29
#define DEPTSIZ_PKTCNT_MASK 0x3FF
#define DEPTSIZ_PKTCNT_SHIFT 19
#define DEPTSIZ_XFERSIZ_MASK 0x1FFFF
// ENDPOINT_DIRECTIONS register has two bits per endpoint. 0, 1 for endpoint 0. 1, 2 for end point 1, etc.
#define USB_EP_DIRECTION(ep) (USBDirection)(2-((GET_REG(USB + GHWCFG1) >> ((ep) * 2)) & 0x3))
#define USB_ENDPOINT_DIRECTIONS_BIDIR 0
#define USB_ENDPOINT_DIRECTIONS_IN 1
#define USB_ENDPOINT_DIRECTIONS_OUT 2
#define USB_START_DELAYUS 10000
#define USB_SFTDISCONNECT_DELAYUS 4000
#define USB_ONOFFSTART_DELAYUS 100
#define USB_RESETWAITFINISH_DELAYUS 1000
#define USB_SFTCONNECT_DELAYUS 250
#define USB_PROGRAMDONE_DELAYUS 10
#define USB_EPCON_ENABLE (1 << 31)
#define USB_EPCON_DISABLE (1 << 30)
#define USB_EPCON_SETD0PID (1 << 28)
#define USB_EPCON_SETNAK (1 << 27)
#define USB_EPCON_CLEARNAK (1 << 26)
#define USB_EPCON_TXFNUM_MASK 0xf
#define USB_EPCON_TXFNUM_SHIFT 22
#define USB_EPCON_STALL (1 << 21)
#define USB_EPCON_TYPE_MASK 0x3
#define USB_EPCON_TYPE_SHIFT 18
#define USB_EPCON_NAKSTS (1 << 17)
#define USB_EPCON_ACTIVE (1 << 15)
#define USB_EPCON_NEXTEP_MASK 0xF
#define USB_EPCON_NEXTEP_SHIFT 11
#define USB_EPCON_MPS_MASK 0x7FF
#define USB_EPINT_INEPNakEff 0x40
#define USB_EPINT_INTknEPMis 0x20
#define USB_EPINT_INTknTXFEmp 0x10
#define USB_EPINT_TimeOUT 0x8
#define USB_EPINT_AHBErr 0x4
#define USB_EPINT_EPDisbld 0x2
#define USB_EPINT_XferCompl 0x1
#define USB_EPINT_Back2BackSetup (1 << 6)
#define USB_EPINT_OUTTknEPDis 0x10
#define USB_EPINT_SetUp 0x8
#define USB_EPINT_EpDisbld 0x1
#define USB_EPINT_NONE 0
#define USB_EPINT_ALL 0xFFFFFFFF
#define USB_2_0 0x0200
#define USB_HIGHSPEED 0
#define USB_FULLSPEED 1
#define USB_LOWSPEED 2
#define USB_FULLSPEED_48_MHZ 3
#define USB_CONTROLEP 0
typedef struct _synopsys_usb_ep_state
{
uint32_t control;
uint32_t tx_size;
uint32_t fifo;
uint32_t interrupt_status;
hwaddr dma_address;
hwaddr dma_buffer;
} synopsys_usb_ep_state;
typedef struct synopsys_usb_state
{
SysBusDevice busdev;
MemoryRegion iomem;
qemu_irq irq;
char *server_host;
uint32_t server_port;
//tcp_usb_state_t tcp_state;
uint32_t pcgcctl;
uint32_t ghwcfg1;
uint32_t ghwcfg2;
uint32_t ghwcfg3;
uint32_t ghwcfg4;
uint32_t gahbcfg;
uint32_t gusbcfg;
uint32_t grxfsiz;
uint32_t gnptxfsiz;
uint32_t gotgctl;
uint32_t gotgint;
uint32_t grstctl;
uint32_t gintmsk;
uint32_t gintsts;
uint32_t dptxfsiz[USB_NUM_FIFOS];
uint32_t dctl;
uint32_t dcfg;
uint32_t dsts;
uint32_t daintmsk;
uint32_t daintsts;
uint32_t diepmsk;
uint32_t doepmsk;
synopsys_usb_ep_state in_eps[USB_NUM_ENDPOINTS];
synopsys_usb_ep_state out_eps[USB_NUM_ENDPOINTS];
uint8_t fifos[0x100 * (USB_NUM_FIFOS+1)];
} synopsys_usb_state;
DeviceState *ipod_touch_init_usb_otg(qemu_irq _irq, uint32_t _hwcfg[4]);
#endif
+28
View File
@@ -0,0 +1,28 @@
#ifndef HW_ARM_IPOD_TOUCH_USB_PHYS_H
#define HW_ARM_IPOD_TOUCH_USB_PHYS_H
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_USB_PHYS "ipodtouch.usbphys"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchUSBPhysState, IPOD_TOUCH_USB_PHYS)
#define REG_OPHYPWR 0x0
#define REG_OPHYCLK 0x4
#define REG_ORSTCON 0x8
#define REG_UNKNOWN1 0x1C
#define REG_OPHYTUNE 0x20
typedef struct IPodTouchUSBPhysState {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t usb_ophypwr;
uint32_t usb_ophyclk;
uint32_t usb_orstcon;
uint32_t usb_unknown1;
uint32_t usb_ophytune;
} IPodTouchUSBPhysState;
#endif
+2 -1
View File
@@ -49,7 +49,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(PL080State, PL080)
struct PL080State {
SysBusDevice parent_obj;
MemoryRegion iomem;
MemoryRegion iomem1;
MemoryRegion iomem2;
uint8_t tc_int;
uint8_t tc_mask;
uint8_t err_int;
+65
View File
@@ -0,0 +1,65 @@
#ifndef IPOD_TOUCH_I2C_H
#define IPOD_TOUCH_I2C_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#define TYPE_IPOD_TOUCH_I2C "ipodtouch.i2c"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchI2CState, IPOD_TOUCH_I2C)
#define I2CCON 0x00 /* I2C Control register */
#define I2CSTAT 0x04 /* I2C Status register */
#define I2CADD 0x08 /* I2C Slave Address register */
#define I2CDS 0x0c /* I2C Data Shift register */
#define I2CLC 0x10 /* I2C Line Control register */
#define IICREG20 0x20
#define SR_MODE 0x0 /* Slave Receive Mode */
#define ST_MODE 0x1 /* Slave Transmit Mode */
#define MR_MODE 0x2 /* Master Receive Mode */
#define MT_MODE 0x3 /* Master Transmit Mode */
#define S5L8900_IICCON_ACKEN (1<<7)
#define S5L8900_IICCON_TXDIV_16 (0<<6)
#define S5L8900_IICCON_TXDIV_512 (1<<6)
#define S5L8900_IICCON_IRQEN (1<<5)
#define S5L8900_IICCON_IRQPEND (1<<4)
#define S5L8900_IICSTAT_START (1<<5)
#define S5L8900_IICSTAT_BUSBUSY (1<<5)
#define S5L8900_IICSTAT_TXRXEN (1<<4)
#define S5L8900_IICSTAT_ARBITR (1<<3)
#define S5L8900_IICSTAT_ASSLAVE (1<<2)
#define S5L8900_IICSTAT_ADDR0 (1<<1)
#define S5L8900_IICSTAT_LASTBIT (1<<0)
#define S5L8900_I2C_REG_MEM_SIZE 0x1000
typedef struct IPodTouchI2CState {
SysBusDevice parent_obj;
MemoryRegion iomem;
I2CBus *bus;
qemu_irq irq;
uint8_t base;
uint8_t control;
uint8_t status;
uint8_t address;
uint8_t datashift;
uint8_t line_ctrl;
uint32_t iicreg20;
uint8_t active;
uint8_t ibmr;
uint8_t data;
} IPodTouchI2CState;
#endif
+80
View File
@@ -0,0 +1,80 @@
#ifndef PL192_H
#define PL192_H
#include "qemu/osdep.h"
#define TYPE_PL192 "pl192"
OBJECT_DECLARE_SIMPLE_TYPE(PL192State, PL192)
#define PL192_INT_SOURCES 32
#define PL192_DAISY_IRQ PL192_INT_SOURCES
#define PL192_NO_IRQ PL192_INT_SOURCES+1
#define PL192_PRIO_LEVELS 16
#define PL192_IRQSTATUS 0x00
#define PL192_FIQSTATUS 0x04
#define PL192_RAWINTR 0x08
#define PL192_INTSELECT 0x0C
#define PL192_INTENABLE 0x10
#define PL192_INTENCLEAR 0x14
#define PL192_SOFTINT 0x18
#define PL192_SOFTINTCLEAR 0x1C
#define PL192_PROTECTION 0x20
#define PL192_SWPRIORITYMASK 0x24
#define PL192_PRIORITYDAISY 0x28
#define PL192_VECTADDR 0xF00
#define PL192_IOMEM_SIZE 0x1000
#define PL190_ITCR 0x300
#define PL190_VECTADDR 0x30
#define PL190_DEFVECTADDR 0x34
#define PL192_IOMEM_SIZE 0x1000
struct PL192State {
SysBusDevice busdev;
MemoryRegion iomem;
/* Control registers */
uint32_t irq_status;
uint32_t fiq_status;
uint32_t rawintr;
uint32_t intselect;
uint32_t intenable;
uint32_t softint;
uint32_t protection;
uint32_t sw_priority_mask;
uint32_t vect_addr[PL192_INT_SOURCES];
uint32_t vect_priority[PL192_INT_SOURCES];
uint32_t address;
/* Currently processed interrupt and
highest priority interrupt */
uint32_t current;
uint32_t current_highest;
/* Priority masking logic */
int32_t stack_i;
uint32_t priority_stack[PL192_PRIO_LEVELS+1];
uint8_t irq_stack[PL192_PRIO_LEVELS+1];
uint32_t priority;
/* Daisy-chain interface */
uint32_t daisy_vectaddr;
uint32_t daisy_priority;
PL192State *daisy_callback;
uint8_t daisy_input;
/* Parent interrupts */
qemu_irq irq;
qemu_irq fiq;
/* Next controller in chain */
PL192State *daisy;
};
DeviceState *pl192_manual_init(char *mem_name, ...);
#endif
+1 -1
View File
@@ -177,7 +177,7 @@ extern "C" {
* supports QEMU_ERROR, this will be reported at compile time; otherwise
* this will be reported at link time due to the missing symbol.
*/
extern G_NORETURN
G_NORETURN extern
void QEMU_ERROR("code path is reachable")
qemu_build_not_reached_always(void);
#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__)
+1
View File
@@ -208,6 +208,7 @@ static void sdl_show_cursor(struct sdl2_console *scon)
static void sdl_grab_start(struct sdl2_console *scon)
{
return;
QemuConsole *con = scon ? scon->dcl.con : NULL;
if (!con || !qemu_console_is_graphic(con)) {