Compare commits

...
92 Commits
Author SHA1 Message Date
Martijn de Vos 7385a19248 Added NOR image 2022-12-24 10:58:47 +01:00
Martijn de Vos 744efa0bcd Renamed iBoot binary 2022-12-24 10:55:28 +01:00
Martijn de Vos 82fab923c7 Added iBoot binary 2022-12-24 10:54:25 +01:00
Martijn de Vos 9fa9bbdb6c Removed hard-coded paths in NAND logic 2022-12-24 09:15:21 +01:00
Martijn de Vos 01bdf8560f Converted various paths to machine args 2022-12-24 09:02:16 +01:00
Martijn de Vos a5ac6115bc Fixed multitouch scrolling 2022-10-02 18:59:55 +02:00
Martijn de Vos beaf938da3 More work on SDIO/WiFi controller 2022-09-25 12:38:09 +02:00
Martijn de Vos c7e9406b3c Some initial work on SDIO 2022-09-24 23:38:45 +02:00
Martijn de Vos 0b62d22a3c Fixed kernel timer 2022-09-22 21:47:47 +02:00
Martijn de Vos 52395b0875 Fixed RTC month 2022-09-22 21:47:18 +02:00
Martijn de Vos 0c313bf18f Power and home buttons working! 2022-09-22 20:32:04 +02:00
Martijn de Vos d2236805ff Multitouch working!!! 2022-09-21 18:50:12 +02:00
Martijn de Vos 21a27376b7 Fixed various aspects of lcd/multitouch 2022-09-20 22:17:47 +02:00
Martijn de Vos c4927a1fa2 Extracted iPod Touch clock to separate file 2022-09-04 19:44:35 +02:00
Martijn de Vos 20d7aa739a Moved memory address definitions to header file 2022-09-04 19:29:38 +02:00
Martijn de Vos 89214ac6f7 Extracted iPod Touch timer to separate file 2022-09-04 19:27:34 +02:00
Martijn de Vos 51aaa2b3ef More work on multitouch 2022-08-30 22:18:22 +02:00
Martijn de Vos 0f67f8b76c Setting appropriate sensor surface width/height 2022-08-30 21:27:51 +02:00
Martijn de Vos e273e9ad77 Fixed additional clock registers 2022-08-30 20:39:52 +02:00
Martijn de Vos 906d257436 Reverted openiBoot loading 2022-08-30 17:18:39 +02:00
Martijn de Vos fac58206ce Corrected values in clock config registers 2022-08-27 19:58:46 +02:00
Martijn de Vos 17f9c55b07 Improvements to NAND 2022-08-21 23:44:25 +02:00
Martijn de Vos eb29b5ecc6 More work on multitouch 2022-08-18 20:41:43 -07:00
Martijn de Vos 72816559dc Fixed bugs with multitouch 2022-08-03 17:49:01 +02:00
Martijn de Vos 5beeaab61b More work on multitouch 2022-08-02 22:33:07 +02:00
Martijn de Vos 76773a441e Some progress on multitouch 2022-08-01 21:44:35 +02:00
Martijn de Vos d6d52a81af GPIO interrupts working! 2022-07-26 21:36:20 +02:00
Martijn de Vos 9f3e33e3cf Worked on RTC 2022-07-17 16:00:46 +02:00
Martijn de Vos 5a767d09b5 Converted SHA1 engine to device 2022-07-09 14:17:18 +02:00
Martijn de Vos c3b7e709ff Converted AES engine to a device 2022-07-09 13:46:45 +02:00
Martijn de Vos d71ff21fe7 Fixed bug in AES engine
We should zero out the IV/key when initializing the hardware. Not doing so might lead to wrong encryption results.
2022-07-09 13:35:10 +02:00
Martijn de Vos e32c75d630 Worked on GUI + accelerometer 2022-07-07 21:52:06 +02:00
Martijn de Vos 9040190818 LCD rendering in kernel is working! 2022-06-30 20:11:10 +02:00
Martijn de Vos 9e9787d0dd More work on LCD 2022-06-16 22:24:46 +02:00
Martijn de Vos 9b6a0e0b1c SPI working! 2022-06-14 20:13:03 +02:00
Martijn de Vos 13ffc18445 Reworked SPI logic 2022-06-12 17:58:54 +02:00
Martijn de Vos 1226cf8de1 Fixed reading from single NAND bank 2022-06-12 13:40:15 +02:00
Martijn de Vos 6bf7ee73cc Removed some print statements 2022-06-11 22:02:50 +02:00
Martijn de Vos f4d8d60530 Reworked SHA1 engine 2022-06-11 21:24:18 +02:00
Martijn de Vos 52adb32e07 SPI protocol working 2022-04-09 23:34:58 +02:00
Martijn de Vos 0b241ee0b6 Support read of many pages simultaneously 2022-04-02 18:34:21 +02:00
Martijn de Vos f59f20f60e Got past TVOut 2022-03-15 21:28:50 +01:00
Martijn de Vos d9380bddc9 More work on ADM/NAND 2022-02-13 13:12:00 +01:00
Martijn de Vos 55b2ddb796 Worked on NAND read/write 2022-02-10 16:01:45 +01:00
Martijn de Vos 58ca7abc51 Fixed timer + tv out memory 2022-02-03 22:19:48 +01:00
Martijn de Vos d5b4b64df3 Minor changes 2022-01-28 22:58:29 +01:00
Martijn de Vos 025b02c2d9 Kernel FMC is working! 2022-01-24 22:41:06 +01:00
Martijn de Vos 19014a5ea2 More work on ADM 2022-01-18 18:23:33 +01:00
Martijn de Vos 7ebda9567e Improved ADM 2022-01-16 20:31:44 +01:00
Martijn de Vos 6e47473564 Modified clock PLLOCK 2022-01-14 13:54:02 +01:00
Martijn de Vos dbfdb9f7e7 Added chip ID module 2022-01-13 14:19:55 +01:00
Martijn de Vos beb1335a71 Fixed CLCD loading 2022-01-13 14:01:10 +01:00
Martijn de Vos b4743e1139 Fixed ADM startup 2022-01-03 15:58:50 +01:00
Martijn de Vos 68edc52052 Refactored ADM/SYSIC into separate files
Fixed ADM powerup
2022-01-03 15:42:01 +01:00
Martijn de Vos b66eef5008 Fixed NAND read + kernel boot 2022-01-02 16:44:25 +01:00
Martijn de Vos fa15006fbb fixed MBX and SDIO 2021-12-31 17:11:53 +01:00
Martijn de Vos 7e2bb1c0b0 Enabled interrupts in I2C 2021-12-30 21:01:12 +01:00
Martijn de Vos 75a3670bd9 I2C working! 2021-12-30 16:25:14 +01:00
Martijn de Vos dc89d3b2bd First version of I2C/PMU 2021-12-30 14:46:53 +01:00
Martijn de Vos 56a309d200 Made it possible to read different NAND pages 2021-12-27 14:42:47 +01:00
Martijn de Vos 88187bb6b6 Improved NAND 2021-12-26 14:26:47 +01:00
Martijn de Vos a6c3de5e45 Cleaned up 2021-12-26 13:28:12 +01:00
Martijn de Vos 7475713599 Refactored NAND/NAND ECC into separate files 2021-12-26 13:28:12 +01:00
Martijn de Vos df09005a18 Changed to 8GB NAND 2021-12-26 13:28:12 +01:00
Martijn de Vos d86dddc5be Added NAND ECC support 2021-12-26 13:28:12 +01:00
Martijn de Vos c333b6490a Started with NAND 2021-12-26 13:28:12 +01:00
Martijn de Vos 486848c749 Fixed SHA1 engine 2021-12-26 13:28:12 +01:00
Martijn de Vos 1d633a1518 Fixed stuff 2021-12-26 13:28:12 +01:00
Martijn de Vos 5703c933c0 Added framebuffer 2021-12-26 13:28:12 +01:00
Martijn de Vos 609e0cadee Fixed timer 2021-12-26 13:28:12 +01:00
Martijn de Vos 59c417d6af Fixed clock bug 2021-12-26 13:28:12 +01:00
Martijn de Vos d84e295289 Drivers are starting!! 2021-12-26 13:28:12 +01:00
Martijn de Vos c3954c2d4e More work (USB, SYSIC) 2021-12-26 13:28:12 +01:00
Martijn de Vos 7dde682632 Kernel boots! 2021-12-26 13:28:12 +01:00
Martijn de Vos 3193772219 Work on 8900 decryption 2021-12-26 13:28:12 +01:00
Martijn de Vos bacfdbbd68 USB OTG 2021-12-26 13:28:12 +01:00
Martijn de Vos d4369e46a0 Getting past peripheral initialization! 2021-12-26 13:28:12 +01:00
Martijn de Vos 3fc8d202b4 More work 2021-12-26 13:28:11 +01:00
Martijn de Vos 2a1917ca1a Added more stuff 2021-12-26 13:28:11 +01:00
Martijn de Vos 0e84b2429d Fixed IRQ 2021-12-26 13:28:11 +01:00
Martijn de Vos de8154086f More work, need to fix VIC 2021-12-26 13:28:11 +01:00
Martijn de Vos 5d7f4d44b7 More work 2021-12-26 13:28:11 +01:00
Martijn de Vos 659aa3fefc More work 2021-12-26 13:28:11 +01:00
Martijn de Vos d4fb06366d Added timer (does not work) 2021-12-26 13:28:11 +01:00
Martijn de Vos 7f7b4749a5 Added clock 2021-12-26 13:28:11 +01:00
Martijn de Vos 35490d71f7 Added VIC 2021-12-26 13:28:11 +01:00
Martijn de Vos e3cda1b547 Correctly mapping memory 2021-12-26 13:28:11 +01:00
Martijn de Vos 58247580d8 Loading device tree 2021-12-26 13:28:11 +01:00
Martijn de Vos 4eb76cd379 More work 2021-12-26 13:28:11 +01:00
Martijn de Vos f11cbe0ec9 Trying to load 32-bit macho 2021-12-26 13:28:11 +01:00
Martijn de Vos 3d39becbc4 Worked on iPod Touch 2g 2021-12-26 13:28:11 +01:00
Martijn de Vos 94963a4588 iPod Touch 1g 2021-12-26 13:28:11 +01:00
58 changed files with 6422 additions and 9 deletions

No files matched your search

+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=y
+51
View File
@@ -1599,3 +1599,54 @@ void AES_decrypt(const unsigned char *in, unsigned char *out,
}
#endif /* AES_ASM */
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
const unsigned long length, const AES_KEY *key,
unsigned char *ivec, const int enc)
{
unsigned long n;
unsigned long len = length;
unsigned char tmp[AES_BLOCK_SIZE];
assert(in && out && key && ivec);
if (enc) {
while (len >= AES_BLOCK_SIZE) {
for(n=0; n < AES_BLOCK_SIZE; ++n)
tmp[n] = in[n] ^ ivec[n];
AES_encrypt(tmp, out, key);
memcpy(ivec, out, AES_BLOCK_SIZE);
len -= AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
if (len) {
for(n=0; n < len; ++n)
tmp[n] = in[n] ^ ivec[n];
for(n=len; n < AES_BLOCK_SIZE; ++n)
tmp[n] = ivec[n];
AES_encrypt(tmp, tmp, key);
memcpy(out, tmp, AES_BLOCK_SIZE);
memcpy(ivec, tmp, AES_BLOCK_SIZE);
}
} else {
while (len >= AES_BLOCK_SIZE) {
memcpy(tmp, in, AES_BLOCK_SIZE);
AES_decrypt(in, out, key);
for(n=0; n < AES_BLOCK_SIZE; ++n)
out[n] ^= ivec[n];
memcpy(ivec, tmp, AES_BLOCK_SIZE);
len -= AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
if (len) {
memcpy(tmp, in, AES_BLOCK_SIZE);
AES_decrypt(tmp, tmp, key);
for(n=0; n < len; ++n)
out[n] = tmp[n] ^ ivec[n];
memcpy(ivec, tmp, AES_BLOCK_SIZE);
}
}
}
BIN
View File
Binary file not shown.
Binary file not shown.
+5
View File
@@ -549,3 +549,8 @@ config ARMSSE
select UNIMP
select SSE_COUNTER
select SSE_TIMER
config IPOD_TOUCH
bool
select IPOD_TOUCH
select PL192
+678
View File
@@ -0,0 +1,678 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu-common.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/ipod_touch.h"
#include "hw/arm/exynos4210.h"
#include "hw/dma/pl080.h"
#include <openssl/aes.h>
static uint64_t tvout_workaround_read(void *opaque, hwaddr addr, unsigned size)
{
return 0;
}
static void tvout_workaround_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
{
}
static const MemoryRegionOps tvout_workaround_ops = {
.read = tvout_workaround_read,
.write = tvout_workaround_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
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 uint32_t align_64k_high(uint32_t addr)
{
return (addr + 0xffffull) & ~0xffffull;
}
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);
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), IBOOT_BASE);
//env->regs[0] = 0x9000000;
//cpu_set_pc(CPU(cpu), LLB_BASE + 0x100);
//cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
}
/*
USB PHYS
*/
static uint64_t s5l8900_usb_phys_read(void *opaque, hwaddr addr, unsigned size)
{
s5l8900_usb_phys_s *s = opaque;
switch(addr)
{
case 0x0: // OPHYPWR
return s->usb_ophypwr;
case 0x4: // OPHYCLK
return s->usb_ophyclk;
case 0x8: // ORSTCON
return s->usb_orstcon;
case 0x20: // OPHYTUNE
return s->usb_ophytune;
default:
fprintf(stderr, "%s: read invalid location 0x%08x\n", __func__, addr);
return 0;
}
return 0;
}
static void s5l8900_usb_phys_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
s5l8900_usb_phys_s *s = opaque;
switch(addr)
{
case 0x0: // OPHYPWR
s->usb_ophypwr = val;
return;
case 0x4: // OPHYCLK
s->usb_ophyclk = val;
return;
case 0x8: // ORSTCON
s->usb_orstcon = val;
return;
case 0x20: // OPHYTUNE
s->usb_ophytune = val;
return;
default:
//hw_error("%s: write invalid location 0x%08x.\n", __func__, offset);
fprintf(stderr, "%s: write invalid location 0x%08x\n", __func__, addr);
}
}
static const MemoryRegionOps usb_phys_ops = {
.read = s5l8900_usb_phys_read,
.write = s5l8900_usb_phys_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
/*
MBX
*/
static uint64_t s5l8900_mbx_read(void *opaque, hwaddr addr, unsigned size)
{
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
switch(addr)
{
case 0x12c:
return 0x100;
case 0xf00:
return (1 << 0x18) | 0x10000; // seems to be some kind of identifier
case 0x1020:
return 0x10000;
default:
break;
}
return 0;
}
static void s5l8900_mbx_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
// do nothing
}
static const MemoryRegionOps mbx_ops = {
.read = s5l8900_mbx_read,
.write = s5l8900_mbx_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
DriveInfo *dinfo;
allocate_ram(sysmem, "sram1", SRAM1_MEM_BASE, 0x10000);
// allocate UART ram
allocate_ram(sysmem, "ram", RAM_MEM_BASE, 0x8000000);
// 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", VROM_MEM_BASE, 0x10000);
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
}
// patch the address table to point to our own routines
uint32_t *data = malloc(4);
data[0] = LLB_BASE + 0x80;
address_space_rw(nsas, 0x2000008c, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
data[0] = LLB_BASE + 0x100;
address_space_rw(nsas, 0x20000090, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
// load iBoot
file_data = NULL;
if (g_file_get_contents(nms->iboot_path, (char **)&file_data, &fsize, NULL)) {
allocate_ram(sysmem, "iboot", IBOOT_BASE, 0x400000);
address_space_rw(nsas, IBOOT_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
}
// // load LLB
// file_data = NULL;
// if (g_file_get_contents("/Users/martijndevos/Documents/ipod_touch_emulation/LLB.n45ap.RELEASE", (char **)&file_data, &fsize, NULL)) {
// allocate_ram(sysmem, "llb", LLB_BASE, align_64k_high(fsize));
// address_space_rw(nsas, LLB_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
// }
allocate_ram(sysmem, "edgeic", EDGEIC_MEM_BASE, 0x1000);
allocate_ram(sysmem, "watchdog", WATCHDOG_MEM_BASE, align_64k_high(0x1));
allocate_ram(sysmem, "iis0", IIS0_MEM_BASE, align_64k_high(0x1));
allocate_ram(sysmem, "iis1", IIS1_MEM_BASE, align_64k_high(0x1));
allocate_ram(sysmem, "iis2", IIS2_MEM_BASE, align_64k_high(0x1));
allocate_ram(sysmem, "mpvd", MPVD_MEM_BASE, 0x70000);
allocate_ram(sysmem, "h264bpd", H264BPD_MEM_BASE, 4096);
allocate_ram(sysmem, "framebuffer", FRAMEBUFFER_MEM_BASE, align_64k_high(4 * 320 * 480));
// setup 1MB NOR
dinfo = drive_get(IF_PFLASH, 0, 0);
if (!dinfo) {
printf("A NOR image must be given with the -pflash parameter\n");
abort();
}
if(!pflash_cfi02_register(NOR_MEM_BASE, "nor", 1024 * 1024, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 4096, 1, 2, 0x00bf, 0x273f, 0x0, 0x0, 0x555, 0x2aa, 0)) {
printf("Error registering NOR flash!\n");
abort();
}
}
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)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
g_strlcpy(nms->bootrom_path, value, sizeof(nms->bootrom_path));
}
static char *ipod_touch_get_iboot_path(Object *obj, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
return g_strdup(nms->iboot_path);
}
static void ipod_touch_set_iboot_path(Object *obj, const char *value, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
g_strlcpy(nms->iboot_path, value, sizeof(nms->iboot_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)
{
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 S5L8900 bootrom binary");
object_property_add_str(obj, "iboot", ipod_touch_get_iboot_path, ipod_touch_set_iboot_path);
object_property_set_description(obj, "iboot", "Path to the iBoot binary");
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 / S5L8900_VIC_SIZE][n % S5L8900_VIC_SIZE];
}
static uint32_t s5l8900_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;
// 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, S5L8900_TIMER1_IRQ));
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));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G5_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G6_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);
dev = exynos4210_uart_create(UART0_MEM_BASE, 256, 0, serial_hd(0), nms->irq[0][24]);
if (!dev) {
printf("Failed to create uart0 device!\n");
abort();
}
dev = exynos4210_uart_create(UART1_MEM_BASE, 256, 1, serial_hd(1), nms->irq[0][25]);
if (!dev) {
printf("Failed to create uart1 device!\n");
abort();
}
dev = exynos4210_uart_create(UART2_MEM_BASE, 256, 2, serial_hd(2), nms->irq[0][26]);
if (!dev) {
printf("Failed to create uart2 device!\n");
abort();
}
dev = exynos4210_uart_create(UART3_MEM_BASE, 256, 3, serial_hd(3), nms->irq[0][27]);
if (!dev) {
printf("Failed to create uart3 device!\n");
abort();
}
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);
sysbus_create_simple("s5l8900spi", SPI0_MEM_BASE, s5l8900_get_irq(nms, S5L8900_SPI0_IRQ));
set_spi_base(1);
sysbus_create_simple("s5l8900spi", SPI1_MEM_BASE, s5l8900_get_irq(nms, S5L8900_SPI1_IRQ));
set_spi_base(2);
dev = sysbus_create_simple("s5l8900spi", SPI2_MEM_BASE, s5l8900_get_irq(nms, S5L8900_SPI2_IRQ));
S5L8900SPIState *spi2_state = S5L8900SPI(dev);
spi2_state->mt->sysic = sysic_state;
spi2_state->mt->gpio_state = gpio_state;
nms->spi2_state = spi2_state;
ipod_touch_memory_setup(machine, sysmem, nsas);
// init LCD
dev = qdev_new("ipodtouch.lcd");
IPodTouchLCDState *lcd_state = IPOD_TOUCH_LCD(dev);
lcd_state->sysmem = sysmem;
lcd_state->mt = spi2_state->mt;
nms->lcd_state = lcd_state;
busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_LCD_IRQ));
memory_region_add_subregion(sysmem, DISPLAY_MEM_BASE, &lcd_state->iomem);
sysbus_realize(busdev, &error_fatal);
// init AES engine
dev = qdev_new("ipodtouch.aes");
S5L8900AESState *aes_state = IPOD_TOUCH_AES(dev);
nms->aes_state = aes_state;
memory_region_add_subregion(sysmem, AES_MEM_BASE, &aes_state->iomem);
// init SHA1 engine
dev = qdev_new("ipodtouch.sha1");
S5L8900SHA1State *sha1_state = IPOD_TOUCH_SHA1(dev);
nms->sha1_state = sha1_state;
memory_region_add_subregion(sysmem, SHA1_MEM_BASE, &sha1_state->iomem);
// init 8900 engine
MemoryRegion *iomem = g_new(MemoryRegion, 1);
memory_region_init_io(iomem, OBJECT(s), &engine_8900_ops, nsas, "8900engine", 0x100);
memory_region_add_subregion(sysmem, ENGINE_8900_MEM_BASE, iomem);
// init NAND flash
dev = qdev_new("itnand");
ITNandState *nand_state = ITNAND(dev);
nand_state->nand_path = &nms->nand_path;
nms->nand_state = nand_state;
memory_region_add_subregion(sysmem, NAND_MEM_BASE, &nand_state->iomem);
// init NAND ECC module
dev = qdev_new("itnand_ecc");
ITNandECCState *nand_ecc_state = ITNANDECC(dev);
nms->nand_ecc_state = nand_ecc_state;
busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_NAND_ECC_IRQ));
memory_region_add_subregion(sysmem, NAND_ECC_MEM_BASE, &nand_ecc_state->iomem);
// init USB OTG
dev = ipod_touch_init_usb_otg(nms->irq[0][13], s5l8900_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 USB PHYS
s5l8900_usb_phys_s *usb_state = malloc(sizeof(s5l8900_usb_phys_s));
nms->usb_phys = usb_state;
usb_state->usb_ophypwr = 0;
usb_state->usb_ophyclk = 0;
usb_state->usb_orstcon = 0;
usb_state->usb_ophytune = 0;
iomem = g_new(MemoryRegion, 1);
memory_region_init_io(iomem, OBJECT(s), &usb_phys_ops, usb_state, "usbphys", 0x40);
memory_region_add_subregion(sysmem, USBPHYS_MEM_BASE, iomem);
// init 8900 OPS
allocate_ram(sysmem, "8900ops", LLB_BASE, 0x1000);
// patch the instructions related to 8900 decryption
uint32_t *data = malloc(sizeof(uint32_t) * 2);
data[0] = 0xe3b00001; // MOVS R0, #1
data[1] = 0xe12fff1e; // BX LR
address_space_rw(nsas, LLB_BASE + 0x80, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint32_t) * 2, 1);
/*
load the decryption logic in memory. These bytes correspond to the following ARMv6 instructions:
LDR r1,[pc,#0x100]
STR r0,[r1]
MOVS R0, #1
BX lr
*/
data = malloc(sizeof(uint32_t) * 4);
data[0] = 0xE59F1100; // LDR r1,[pc,#0x100]
data[1] = 0xE5810000; // STR r0,[r1]
data[2] = 0xE3B00001; // MOVS R0, #1
data[3] = 0xE12FFF1E; // BX lr
address_space_rw(nsas, LLB_BASE + 0x100, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint32_t) * 4, 1);
// contains some constants
data = malloc(4);
data[0] = ENGINE_8900_MEM_BASE; // engine base address
address_space_rw(nsas, LLB_BASE + 0x208, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
// 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->iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_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_MEM_BASE, &pl080_2->iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_DMAC1_IRQ));
// Init I2C
dev = qdev_new("ipodtouch.i2c");
IPodTouchI2CState *i2c_state = IPOD_TOUCH_I2C(dev);
nms->i2c0_state = i2c_state;
busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_I2C0_IRQ));
memory_region_add_subregion(sysmem, I2C0_MEM_BASE, &i2c_state->iomem);
// init the accelerometer
I2CSlave *accelerometer = i2c_slave_create_simple(i2c_state->bus, "lis302dl", 0x1D);
dev = qdev_new("ipodtouch.i2c");
i2c_state = IPOD_TOUCH_I2C(dev);
nms->i2c1_state = i2c_state;
busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_I2C1_IRQ));
memory_region_add_subregion(sysmem, I2C1_MEM_BASE, &i2c_state->iomem);
// init the PMU
I2CSlave *pmu = i2c_slave_create_simple(i2c_state->bus, "pcf50633", 0x73);
// init the ADM
dev = qdev_new("ipodtouch.adm");
IPodTouchADMState *adm_state = IPOD_TOUCH_ADM(dev);
adm_state->nand_state = nand_state;
nms->adm_state = adm_state;
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_ADM_IRQ));
memory_region_add_subregion(sysmem, ADM_MEM_BASE, &adm_state->iomem);
// init MBX
iomem = g_new(MemoryRegion, 1);
memory_region_init_io(iomem, OBJECT(nms), &mbx_ops, NULL, "mbx", 0x1000000);
memory_region_add_subregion(sysmem, MBX_MEM_BASE, iomem);
// 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 instances
dev = qdev_new("ipodtouch.tvout");
IPodTouchTVOutState *tvout_state = IPOD_TOUCH_TVOUT(dev);
tvout_state->index = 1;
nms->tvout1_state = tvout_state;
memory_region_add_subregion(sysmem, TVOUT1_MEM_BASE, &tvout_state->iomem);
dev = qdev_new("ipodtouch.tvout");
tvout_state = IPOD_TOUCH_TVOUT(dev);
tvout_state->index = 2;
nms->tvout2_state = tvout_state;
memory_region_add_subregion(sysmem, TVOUT2_MEM_BASE, &tvout_state->iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_TVOUT_SDO_IRQ));
dev = qdev_new("ipodtouch.tvout");
tvout_state = IPOD_TOUCH_TVOUT(dev);
tvout_state->index = 3;
nms->tvout3_state = tvout_state;
memory_region_add_subregion(sysmem, TVOUT3_MEM_BASE, &tvout_state->iomem);
// setup workaround for TVOut
iomem = g_new(MemoryRegion, 1);
memory_region_init_io(iomem, OBJECT(nms), &tvout_workaround_ops, NULL, "tvoutworkaround", 0x4);
memory_region_add_subregion(sysmem, TVOUT_WORKAROUND_MEM_BASE, iomem);
qemu_register_reset(ipod_touch_cpu_reset, nms);
qemu_add_kbd_event_handler(ipod_touch_key_event, spi2_state->mt);
}
static void ipod_touch_machine_class_init(ObjectClass *obj, void *data)
{
MachineClass *mc = MACHINE_CLASS(obj);
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)
+1
View File
@@ -0,0 +1 @@
#include "hw/arm/ipod_touch_8900_engine.h"
+276
View File
@@ -0,0 +1,276 @@
#include "hw/arm/ipod_touch_adm.h"
#include "hw/qdev-properties.h"
#include "hw/arm/ipod_touch_nand.h"
#include "qapi/error.h"
static void set_bank(ITNandState *s, uint8_t activate_bank) {
for(int bank = 0; bank < 8; bank++) {
// clear bit, toggle if it is active
s->fmctrl0 &= ~(1 << (bank + 1));
if(bank == activate_bank) {
s->fmctrl0 ^= 1 << (bank + 1);
}
}
}
static uint64_t ipod_touch_adm_read(void *opaque, hwaddr offset, unsigned size)
{
//IPodTouchADMState *s = (IPodTouchADMState *)opaque;
//fprintf(stderr, "s5l8900_adm_read(): offset = 0x%08x\n", offset);
switch (offset) {
case ADM_CTRL: // this seems to be the control register
return 0x2; // this seems to indicate that the device is ready
case ADM_CTRL2:
return 0x10; // indicates that a upload event has been finished
default:
break;
}
return 0;
}
static void ipod_touch_adm_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchADMState *s = (IPodTouchADMState *)opaque;
//fprintf(stderr, "s5l8900_adm_write: offset = 0x%08x, val = 0x%08x\n", offset, value);
switch(offset) {
case ADM_CTRL:
if(value == 0x3) {
// some kind of start-up command?
// write some bits to data2_sec_addr to indicate that the device is started
uint32_t *data = (uint32_t *) malloc(sizeof(uint32_t));
data[0] = 0x50;
address_space_rw(&s->downstream_as, s->data2_sec_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
// dunno, write some bytes to data4_sec_addr to indicate that the NAND banks are ready
data = (uint32_t *) malloc(sizeof(uint32_t) * 8);
for(int i = 0; i < 8; i++) {
data[i] = NAND_CHIP_ID;
}
address_space_rw(&s->downstream_as, s->data3_sec_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 8 * sizeof(uint32_t), 1);
}
break;
case ADM_CTRL2:
if(value == 0x2) {
// read the command and initialize the right device
uint32_t page;
uint16_t num_pages;
uint8_t bank;
uint32_t *buf = malloc(80);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x24, MEMTXATTRS_UNSPECIFIED, buf, 80);
int cmd = *buf;
// printf("Setting command: 0x%08x\n", cmd);
// for(int i = 0; i < 20; i++) {
// printf("0x%08x ", buf[i]);
// }
// printf("\n");
switch(cmd) {
case 0x200:
// read multiple pages simultaneously from the same bank
s->nand_state->reading_multiple_pages = true;
buf = malloc(2);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x28, MEMTXATTRS_UNSPECIFIED, buf, 2);
num_pages = *buf;
num_pages = ( (((num_pages) >> 8) & 0x00FF) | (((num_pages) << 8) & 0xFF00) ); // swap endianness
//printf("Reading %d pages at once, ", num_pages);
buf = malloc(4);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244, MEMTXATTRS_UNSPECIFIED, buf, 4);
page = *buf;
page = ((page>>24)&0xff) | // move byte 3 to byte 0
((page<<8)&0xff0000) | // move byte 1 to byte 2
((page>>8)&0xff00) | // move byte 2 to byte 1
((page<<24)&0xff000000); // byte 0 to byte 3
//printf("starting with page %d\n", page);
uint16_t ops = num_pages / 8;
for(int op = 0; op < ops; op++) {
for(int i = 0; i < 8; i++) {
s->nand_state->pages_to_read[op * 8 + i] = page;
s->nand_state->banks_to_read[op * 8 + i] = i;
}
page++;
}
s->nand_state->fmdnum = (num_pages * 0x800);
s->nand_state->cur_bank_reading = -1;
for(int i = 0; i < num_pages; i++) {
uint8_t *sbuf = malloc(0xC);
sbuf[10] = 0xFF;
address_space_rw(&s->downstream_as, s->data3_sec_addr + i * 0xC, MEMTXATTRS_UNSPECIFIED, sbuf, 0xC, 1);
}
break;
case 0x300:
// seems to be the NAND read command, read the page(s) + bank and instruct the flash device
s->nand_state->reading_multiple_pages = false;
buf = malloc(2);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x28, MEMTXATTRS_UNSPECIFIED, buf, 2);
num_pages = *buf;
num_pages = ( (((num_pages) >> 8) & 0x00FF) | (((num_pages) << 8) & 0xFF00) ); // swap endianness
if(num_pages == 1) {
// TODO this can probably be refactored to re-use the logic to read multiple pages!
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x44, MEMTXATTRS_UNSPECIFIED, buf, 1);
bank = *buf;
buf = malloc(4);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244, MEMTXATTRS_UNSPECIFIED, buf, 4);
page = *buf;
page = ((page>>24)&0xff) | // move byte 3 to byte 0
((page<<8)&0xff0000) | // move byte 1 to byte 2
((page>>8)&0xff00) | // move byte 2 to byte 1
((page<<24)&0xff000000); // byte 0 to byte 3
//printf("Reading single page: %d (bank: %d)\n", page, bank);
// set the bank, page, and operation.
set_bank(s->nand_state, bank);
memory_region_dispatch_write(&s->nand_state->iomem, NAND_FMDNUM, 0x800 - 1, MO_32, MEMTXATTRS_UNSPECIFIED);
memory_region_dispatch_write(&s->nand_state->iomem, NAND_FMADDR0, page << 16, MO_32, MEMTXATTRS_UNSPECIFIED);
memory_region_dispatch_write(&s->nand_state->iomem, NAND_FMADDR1, (page >> 16) & 0xFF, MO_32, MEMTXATTRS_UNSPECIFIED);
memory_region_dispatch_write(&s->nand_state->iomem, NAND_CMD, NAND_CMD_READ, MO_32, MEMTXATTRS_UNSPECIFIED);
// write the spare of the page to the 3rd data section
nand_set_buffered_page(s->nand_state, page);
address_space_rw(&s->downstream_as, s->data3_sec_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->nand_state->page_spare_buffer, NAND_BYTES_PER_SPARE, 1);
}
else if(num_pages > 1) {
// read scattered pages
// printf("Reading %d scattered pages\n", num_pages);
s->nand_state->reading_multiple_pages = true;
for(int i = 0; i < num_pages; i++) {
buf = malloc(4);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244 + 4 * i, MEMTXATTRS_UNSPECIFIED, buf, 4);
page = *buf;
page = ((page>>24)&0xff) | // move byte 3 to byte 0
((page<<8)&0xff0000) | // move byte 1 to byte 2
((page>>8)&0xff00) | // move byte 2 to byte 1
((page<<24)&0xff000000); // byte 0 to byte 3
buf = malloc(1);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x44 + i, MEMTXATTRS_UNSPECIFIED, buf, 1);
bank = *buf;
// printf("Page: %d, bank: %d\n", page, bank);
s->nand_state->pages_to_read[i] = page;
s->nand_state->banks_to_read[i] = bank;
}
s->nand_state->fmdnum = (num_pages * 0x800);
s->nand_state->cur_bank_reading = -1;
for(int i = 0; i < num_pages; i++) {
uint8_t *sbuf = malloc(0xC);
sbuf[10] = 0xFF;
address_space_rw(&s->downstream_as, s->data3_sec_addr + i * 0xC, MEMTXATTRS_UNSPECIFIED, sbuf, 0xC, 1);
}
}
break;
case 0x500:
// writing a page
buf = malloc(1);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x44, MEMTXATTRS_UNSPECIFIED, buf, 1);
bank = *buf;
buf = malloc(4);
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244, MEMTXATTRS_UNSPECIFIED, buf, 4);
page = *buf;
page = ((page>>24)&0xff) | // move byte 3 to byte 0
((page<<8)&0xff0000) | // move byte 1 to byte 2
((page>>8)&0xff00) | // move byte 2 to byte 1
((page<<24)&0xff000000); // byte 0 to byte 3
// set the bank, page, and operation.
//printf("Activating bank for writing: %d, page: %d\n", bank, page);
set_bank(s->nand_state, bank);
nand_set_buffered_page(s->nand_state, page);
s->nand_state->fmdnum = NAND_BYTES_PER_PAGE;
s->nand_state->is_writing = true;
break;
default:
printf("Unrecognized ADM command: %d\n", cmd);
break;
}
qemu_irq_raise(s->irq);
}
if((value & 0x2) == 0) {
qemu_irq_lower(s->irq);
}
break;
case ADM_CODE_SEC_ADDR:
s->code_sec_addr = value;
break;
case ADM_DATA1_SEC_ADDR:
s->data1_sec_addr = value;
break;
case ADM_DATA2_SEC_ADDR:
s->data2_sec_addr = value;
break;
case ADM_DATA3_SEC_ADDR:
s->data3_sec_addr = value;
break;
default:
break;
}
}
static const MemoryRegionOps ipod_touch_adm_ops = {
.read = ipod_touch_adm_read,
.write = ipod_touch_adm_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void ipod_touch_adm_realize(DeviceState *dev, Error **errp)
{
IPodTouchADMState *s = IPOD_TOUCH_ADM(dev);
if (!s->downstream) {
error_setg(errp, "ADM 'downstream' link not set");
return;
}
address_space_init(&s->downstream_as, s->downstream, "adm-downstream");
}
static Property adm_properties[] = {
DEFINE_PROP_LINK("downstream", IPodTouchADMState, downstream,
TYPE_MEMORY_REGION, MemoryRegion *),
DEFINE_PROP_END_OF_LIST(),
};
static void ipod_touch_adm_init(Object *obj)
{
IPodTouchADMState *s = IPOD_TOUCH_ADM(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &ipod_touch_adm_ops, s, TYPE_IPOD_TOUCH_ADM, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void ipod_touch_adm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ipod_touch_adm_realize;
device_class_set_props(dc, adm_properties);
}
static const TypeInfo ipod_touch_adm_type_info = {
.name = TYPE_IPOD_TOUCH_ADM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IPodTouchADMState),
.instance_init = ipod_touch_adm_init,
.class_init = ipod_touch_adm_class_init,
};
static void ipod_touch_adm_register_types(void)
{
type_register_static(&ipod_touch_adm_type_info);
}
type_init(ipod_touch_adm_register_types)
+135
View File
@@ -0,0 +1,135 @@
#include "hw/arm/ipod_touch_aes.h"
static uint64_t s5l8900_aes_read(void *opaque, hwaddr offset, unsigned size)
{
struct S5L8900AESState *aesop = (struct S5L8900AESState *)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 s5l8900_aes_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
struct S5L8900AESState *aesop = (struct S5L8900AESState *)opaque;
static uint8_t keylenop = 0;
uint8_t *inbuf;
uint8_t *buf;
// fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
switch(offset) {
case AES_GO:
inbuf = (uint8_t *)malloc(aesop->insize);
cpu_physical_memory_read((aesop->inaddr - 0x80000000), inbuf, aesop->insize);
switch(aesop->keytype) {
case AESGID:
fprintf(stderr, "%s: No support for GID key\n", __func__);
return;
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, 0x20 * 8, &aesop->decryptKey);
break;
}
buf = (uint8_t *) malloc(aesop->insize);
AES_cbc_encrypt(inbuf, buf, aesop->insize, &aesop->decryptKey, (uint8_t *)aesop->ivec, aesop->operation);
cpu_physical_memory_write((aesop->outaddr - 0x80000000), buf, aesop->insize);
memset(aesop->custkey, 0, 0x20);
memset(aesop->ivec, 0, 0x10);
free(inbuf);
free(buf);
keylenop = 0;
aesop->outsize = aesop->insize;
aesop->status = 0xf;
break;
case AES_KEYLEN:
if(keylenop == 1) {
aesop->operation = value;
}
keylenop++;
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 = s5l8900_aes_read,
.write = s5l8900_aes_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_aes_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
S5L8900AESState *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));
}
static void s5l8900_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(S5L8900AESState),
.instance_init = s5l8900_aes_init,
.class_init = s5l8900_aes_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_aes_info);
}
type_init(ipod_touch_machine_types)
+50
View File
@@ -0,0 +1,50 @@
#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 0x4:
return (CHIP_REVISION << 24);
default:
break;
}
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)
{
DeviceState *dev = DEVICE(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, 0x10);
sysbus_init_mmio(sbd, &s->iomem);
}
static void ipod_touch_chipid_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
}
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)
+112
View File
@@ -0,0 +1,112 @@
#include "hw/arm/ipod_touch_clock.h"
static void s5l8900_clock1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
switch (addr) {
case CLOCK1_PLLLOCK:
s->plllock = val;
break;
default:
break;
}
}
static uint64_t s5l8900_clock1_read(void *opaque, hwaddr addr, unsigned size)
{
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
switch (addr) {
case CLOCK1_CONFIG0:
return s->config0;
case CLOCK1_CONFIG1:
return s->config1;
case CLOCK1_CONFIG2:
return s->config2;
case CLOCK1_PLL0CON:
return s->pll0con;
case CLOCK1_PLL1CON:
return s->pll1con;
case CLOCK1_PLL2CON:
return s->pll2con;
case CLOCK1_PLL3CON:
return s->pll3con;
case CLOCK1_PLLLOCK:
return s->plllock;
case CLOCK1_PLLMODE:
return 0x000a003a; // extracted from iPod Touch
default:
break;
}
return 0;
}
static const MemoryRegionOps clock1_ops = {
.read = s5l8900_clock1_read,
.write = s5l8900_clock1_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);
s->plllock = 1 | 2 | 4 | 8;
uint64_t config0 = 0x0;
config0 |= (1 << 12); // set the clock PPL to index 1
config0 |= (1 << 24); // indicate that we have a memory divisor
config0 |= (2 << 16); // set the memory divisor to two
s->config0 = config0;
uint64_t config1 = 0x0;
config1 |= (1 << 12); // set the bus PPL to index 1
config1 |= (1 << 24); // indicate that we have a bus divisor
config1 |= (3 << 16); // set the bus divisor to three
config1 |= (1 << 8); // indicate that unknown has a divisor
config1 |= 3; // set the unknown divisor 1 to 3
config1 |= (0 << 4); // set the unknown divisor 2 to 0
config1 |= (1 << 20); // set the peripheral factor to 1
config1 |= (1 << 14); // unknown
config1 |= (1 << 28); // set some PPL index to 1
config1 |= (1 << 30); // unknown
s->config1 = config1;
uint64_t config2 = 0x0;
config2 |= (3 << 28); // set the peripheral PPL to index 3
config2 |= (1 << 24); // indicate that the display has a divisor
config2 |= (1 << 16); // set the display divisor to 1
s->config2 = config2;
// set the PLL configurations (MDIV, PDIV, SDIV)
s->pll0con = (80 << 8) | (8 << 24) | 0;
s->pll1con = (103 << 8) | (6 << 24) | 0;
s->pll2con = (156 << 8) | (53 << 24) | 2;
s->pll3con = (72 << 8) | (8 << 24) | 1;
memory_region_init_io(&s->iomem, obj, &clock1_ops, s, "clock", 0x1000);
}
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)
+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 0x2c4:
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", 0x10000);
}
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)
+301
View File
@@ -0,0 +1,301 @@
#include "hw/arm/ipod_touch_lcd.h"
#include "ui/pixel_ops.h"
#include "ui/console.h"
#include "hw/display/framebuffer.h"
static uint64_t s5l8900_lcd_read(void *opaque, hwaddr addr, unsigned size)
{
// fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
switch(addr)
{
case 0x4:
return s->lcd_con;
case 0x8:
return s->lcd_con2;
case 0x14:
return s->unknown1;
case 0x18:
return s->unknown2;
case 0x20:
return s->wnd_con;
case 0x200:
return s->vid_con0;
case 0x204:
return s->vid_con1;
case 0x20C:
return s->vidt_con0;
case 0x210:
return s->vidt_con1;
case 0x214:
return s->vidt_con2;
case 0x218:
return s->vidt_con3;
case 0x58:
return s->w1_hspan;
case 0x5c:
return s->w1_display_depth_info;
case 0x60:
return s->w1_framebuffer_base;
case 0x64:
return s->w1_display_resolution_info;
case 0x68:
return s->w1_qlen;
case 0x70:
return s->w2_hspan;
case 0x74:
return s->w2_display_depth_info;
case 0x78:
return s->w2_framebuffer_base;
case 0x7c:
return s->w2_display_resolution_info;
case 0x80:
return s->w2_qlen;
default:
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
break;
}
return 0;
}
static void s5l8900_lcd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
// fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
switch(addr) {
case 0x4:
s->lcd_con = val;
break;
case 0x8:
s->lcd_con2 = val;
break;
case 0x14:
s->unknown1 = val;
break;
case 0x18:
s->unknown2 = val;
qemu_irq_lower(s->irq);
break;
case 0x20:
s->wnd_con = val;
break;
case 0x200:
s->vid_con0 = val;
break;
case 0x204:
s->vid_con1 = val;
break;
case 0x20C:
s->vidt_con0 = val;
break;
case 0x210:
s->vidt_con1 = val;
break;
case 0x214:
s->vidt_con2 = val;
break;
case 0x218:
s->vidt_con3 = val;
break;
case 0x58:
s->w1_hspan = val;
break;
case 0x5c:
s->w1_display_depth_info = val;
break;
case 0x60:
s->w1_framebuffer_base = val;
break;
case 0x64:
s->w1_display_resolution_info = val;
break;
case 0x68:
s->w1_qlen = val;
break;
case 0x70:
s->w2_hspan = val;
break;
case 0x74:
s->w2_display_depth_info = val;
break;
case 0x78:
s->w2_framebuffer_base = val;
break;
case 0x7c:
s->w2_display_resolution_info = val;
break;
case 0x80:
s->w2_qlen = 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 = s5l8900_lcd_read,
.write = s5l8900_lcd_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const GraphicHwOps s5l8900_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 s5l8900_lcd_realize(DeviceState *dev, Error **errp)
{
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
s->con = graphic_console_init(dev, 0, &s5l8900_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 s5l8900_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", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void s5l8900_lcd_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = s5l8900_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 = s5l8900_lcd_init,
.class_init = s5l8900_lcd_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_lcd_info);
}
type_init(ipod_touch_machine_types)
+63
View File
@@ -0,0 +1,63 @@
#include "hw/arm/ipod_touch_lcd_panel.h"
static uint32_t ipod_touch_lcd_panel_transfer(SSIPeripheral *dev, uint32_t value)
{
IPodTouchLCDPanelState *s = IPOD_TOUCH_LCD_PANEL(dev);
if(!s->cur_cmd && (value == 0x95 || value == 0xDA || value == 0xDB || value == 0xDC)) {
// this is a command -> set it
s->cur_cmd = value;
return 0x0;
}
if(s->cur_cmd) {
uint32_t res = 0;
switch(s->cur_cmd) {
case 0x95:
res = 0x1;
break;
case 0xDA:
res = 0x71;
break;
case 0xDB:
res = 0xC2;
break;
case 0xDC:
res = 0x0;
break;
default:
break;
}
s->cur_cmd = 0;
return res;
}
return 0x0;
}
static void ipod_touch_lcd_panel_realize(SSIPeripheral *d, Error **errp)
{
}
static void ipod_touch_lcd_panel_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
k->realize = ipod_touch_lcd_panel_realize;
k->transfer = ipod_touch_lcd_panel_transfer;
}
static const TypeInfo ipod_touch_lcd_panel_type_info = {
.name = TYPE_IPOD_TOUCH_LCD_PANEL,
.parent = TYPE_SSI_PERIPHERAL,
.instance_size = sizeof(IPodTouchLCDPanelState),
.class_init = ipod_touch_lcd_panel_class_init,
};
static void ipod_touch_lcd_panel_register_types(void)
{
type_register_static(&ipod_touch_lcd_panel_type_info);
}
type_init(ipod_touch_lcd_panel_register_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)
+415
View File
@@ -0,0 +1,415 @@
#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) {
printf("Will read frame!\n");
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);
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[4] |= (1 << 27); // the multitouch interrupt bit is in group 4 (32 interrupts per group), and the 27th of the 4th group
qemu_irq_raise(s->sysic->gpio_irqs[4]);
}
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)
+246
View File
@@ -0,0 +1,246 @@
#include "hw/arm/ipod_touch_nand.h"
static int get_bank(ITNandState *s) {
uint32_t bank_bitmap = (s->fmctrl0 >> 1) & 0xFF;
for(int bank = 0; bank < NAND_NUM_BANKS; bank++) {
if((bank_bitmap & (1 << bank)) != 0) {
return bank;
}
}
return -1;
}
static void set_bank(ITNandState *s, uint32_t activate_bank) {
for(int bank = 0; bank < 8; bank++) {
// clear bit, toggle if it is active
s->fmctrl0 &= ~(1 << (bank + 1));
if(bank == activate_bank) {
s->fmctrl0 ^= 1 << (bank + 1);
}
}
}
void nand_set_buffered_page(ITNandState *s, uint32_t page) {
uint32_t bank = get_bank(s);
if(bank == -1) {
hw_error("Active bank not set while nand_read with page %d is called (reading multiple pages: %d)!", page, s->reading_multiple_pages);
}
if(bank != s->buffered_bank || page != s->buffered_page) {
// refresh the buffered page
uint32_t vpn = page * 8 + bank;
char filename[200];
sprintf(filename, "%s/bank%d/%d.page", s->nand_path, bank, page);
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);
s->page_spare_buffer[0xA] = 0xFF; // make sure we add the FTL mark to an empty page
}
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);
}
s->buffered_page = page;
s->buffered_bank = bank;
// printf("Buffered bank: %d, page: %d\n", s->buffered_bank, s->buffered_page);
}
}
static uint64_t itnand_read(void *opaque, hwaddr addr, unsigned size)
{
ITNandState *s = (ITNandState *) opaque;
if(s->reading_multiple_pages) {
//fprintf(stderr, "%s: reading from 0x%08x\n", __func__, addr);
}
switch (addr) {
case NAND_FMCTRL0:
return s->fmctrl0;
case NAND_FMFIFO:
if(s->cmd == NAND_CMD_ID) {
return NAND_CHIP_ID;
}
else if(s->cmd == NAND_CMD_READSTATUS) {
return (1 << 6);
}
else {
uint32_t read_val = 0;
if(s->reading_multiple_pages) {
// which bank are we at?
if(s->fmdnum % 0x800 == 0) {
s->cur_bank_reading += 1;
//printf("WILL TURN TO BANK %d (cnt: %d)\n", s->cur_bank_reading, s->fmdnum);
set_bank(s, s->banks_to_read[s->cur_bank_reading]);
}
// compute the offset in the page
uint32_t page_offset = s->fmdnum % 0x800;
if(page_offset == 0) { page_offset = 0x800; }
nand_set_buffered_page(s, s->pages_to_read[s->cur_bank_reading]);
//printf("Reading page %d\n", s->pages_to_read[s->cur_bank_reading]);
read_val = ((uint32_t *)s->page_buffer)[(NAND_BYTES_PER_PAGE - page_offset) / 4];
//printf("FMDNUM: %d, offset: %d\n", s->fmdnum, (NAND_BYTES_PER_PAGE - page_offset) / 4);
//printf("Page offset: %d, bytes: 0x%08x\n", page_offset, read_val);
}
else {
uint32_t page = (s->fmaddr1 << 16) | (s->fmaddr0 >> 16);
nand_set_buffered_page(s, page);
//printf("Reading page %d\n", page);
if(s->reading_spare) {
read_val = ((uint32_t *)s->page_spare_buffer)[(NAND_BYTES_PER_SPARE - s->fmdnum - 1) / 4];
} else {
read_val = ((uint32_t *)s->page_buffer)[(NAND_BYTES_PER_PAGE - s->fmdnum - 1) / 4];
}
}
s->fmdnum -= 4;
return read_val;
}
case NAND_FMCSTAT:
return (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12); // this indicates that everything is ready, including our eight banks
case NAND_RSCTRL:
return s->rsctrl;
default:
break;
}
return 0;
}
static void itnand_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
ITNandState *s = (ITNandState *) opaque;
if(s->reading_multiple_pages) {
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
}
switch(addr) {
case NAND_FMCTRL0:
s->fmctrl0 = val;
break;
case NAND_FMCTRL1:
s->fmctrl1 = val;
break;
case NAND_FMADDR0:
s->fmaddr0 = val;
break;
case NAND_FMADDR1:
s->fmaddr1 = val;
break;
case NAND_FMANUM:
s->fmanum = val;
break;
case NAND_CMD:
s->cmd = val;
break;
case NAND_FMDNUM:
if(val == NAND_BYTES_PER_SPARE - 1) {
s->reading_spare = 1;
} else {
s->reading_spare = 0;
}
s->fmdnum = val;
break;
case NAND_FMFIFO:
if(!s->is_writing) {
// printf("%s: NAND_FMFIFO writing while not in writing mode!\n", __func__);
return;
}
//printf("Setting offset %d: %d\n", s->fmdnum, (NAND_BYTES_PER_PAGE - s->fmdnum) / 4);
((uint32_t *)s->page_buffer)[(NAND_BYTES_PER_PAGE - s->fmdnum) / 4] = val;
s->fmdnum -= 4;
if(s->fmdnum == 0) {
// we're done!
s->is_writing = false;
// flush the page buffer to the disk
uint32_t vpn = s->buffered_page * 8 + s->buffered_bank;
//printf("Flushing page %d, bank %d, vpn %d\n", s->buffered_page, s->buffered_bank, vpn);
qemu_mutex_lock(&s->lock);
qemu_mutex_unlock(&s->lock);
{
char filename[200];
sprintf(filename, "%s/bank%d/%d_new.page", s->nand_path, s->buffered_bank, s->buffered_page);
FILE *f = fopen(filename, "wb");
if (f == NULL) { hw_error("Unable to read file!"); }
fwrite(s->page_buffer, sizeof(char), NAND_BYTES_PER_PAGE, f);
fwrite(s->page_spare_buffer, sizeof(char), NAND_BYTES_PER_SPARE, f);
fclose(f);
}
}
break;
case NAND_RSCTRL:
s->rsctrl = val;
break;
default:
break;
}
}
static const MemoryRegionOps nand_ops = {
.read = itnand_read,
.write = itnand_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void itnand_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
ITNandState *s = ITNAND(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &nand_ops, s, "nand", 0x1000);
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);
s->buffered_page = -1;
s->buffered_bank = -1;
qemu_mutex_init(&s->lock);
}
static void itnand_reset(DeviceState *d)
{
ITNandState *s = (ITNandState *) d;
s->fmctrl0 = 0;
s->fmctrl1 = 0;
s->fmaddr0 = 0;
s->fmaddr1 = 0;
s->fmanum = 0;
s->fmdnum = 0;
s->rsctrl = 0;
s->cmd = 0;
s->reading_spare = 0;
s->buffered_page = -1;
}
static void itnand_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->reset = itnand_reset;
}
static const TypeInfo itnand_info = {
.name = TYPE_ITNAND,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ITNandState),
.instance_init = itnand_init,
.class_init = itnand_class_init,
};
static void itnand_register_types(void)
{
type_register_static(&itnand_info);
}
type_init(itnand_register_types)
+77
View File
@@ -0,0 +1,77 @@
#include "hw/arm/ipod_touch_nand_ecc.h"
static uint64_t itnand_ecc_read(void *opaque, hwaddr addr, unsigned size)
{
ITNandECCState *s = (ITNandECCState *) opaque;
//fprintf(stderr, "%s: reading from 0x%08x\n", __func__, addr);
switch (addr) {
case NANDECC_STATUS:
return 0; // TODO: for now, we assume that all ECC operations are successful
default:
break;
}
return 0;
}
static void itnand_ecc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
{
ITNandECCState *s = (ITNandECCState *) opaque;
switch(addr) {
case NANDECC_START:
qemu_irq_raise(s->irq);
break;
case NANDECC_CLEARINT:
qemu_irq_lower(s->irq);
break;
default:
break;
}
}
static const MemoryRegionOps nand_ecc_ops = {
.read = itnand_ecc_read,
.write = itnand_ecc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void itnand_ecc_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
ITNandECCState *s = ITNANDECC(obj);
memory_region_init_io(&s->iomem, OBJECT(s), &nand_ecc_ops, s, "nandecc", 0x100);
sysbus_init_irq(sbd, &s->irq);
}
static void itnand_ecc_reset(DeviceState *d)
{
ITNandECCState *s = (ITNandECCState *) d;
s->data_addr = 0;
s->ecc_addr = 0;
s->status = 0;
s->setup = 0;
}
static void itnand_ecc_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->reset = itnand_ecc_reset;
}
static const TypeInfo itnand_ecc_info = {
.name = TYPE_ITNANDECC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ITNandECCState),
.instance_init = itnand_ecc_init,
.class_init = itnand_ecc_class_init,
};
static void itnand_ecc_register_types(void)
{
type_register_static(&itnand_ecc_info);
}
type_init(itnand_ecc_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)
+105
View File
@@ -0,0 +1,105 @@
#include "hw/arm/ipod_touch_sdio.h"
void sdio_exec_cmd(IPodTouchSDIOState *s)
{
uint32_t addr = (s->arg >> 9) & 0x1ffff;
if(addr == (1 << 8)) {
// reading slot 1 - make sure there is a device here
//s->resp0 = 1;
}
}
static void ipod_touch_sdio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
{
fprintf(stderr, "%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)) {
sdio_exec_cmd(s);
}
break;
case SDIO_ARGU:
s->arg = value;
break;
case SDIO_CSR:
s->csr = value;
break;
case SDIO_IRQMASK:
s->irq_mask = value;
break;
default:
break;
}
}
static uint64_t ipod_touch_sdio_read(void *opaque, hwaddr addr, unsigned size)
{
fprintf(stderr, "%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_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_IRQMASK:
return s->irq_mask;
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);
memory_region_init_io(&s->iomem, obj, &ipod_touch_sdio_ops, s, TYPE_IPOD_TOUCH_SDIO, 4096);
sysbus_init_mmio(sbd, &s->iomem);
}
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(S5L8900SHA1State *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(S5L8900SHA1State *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 s5l8900_sha1_read(void *opaque, hwaddr offset, unsigned size)
{
S5L8900SHA1State *s = (S5L8900SHA1State *)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 s5l8900_sha1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
S5L8900SHA1State *s = (S5L8900SHA1State *)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 = s5l8900_sha1_read,
.write = s5l8900_sha1_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_sha1_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
DeviceState *dev = DEVICE(sbd);
S5L8900SHA1State *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 s5l8900_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(S5L8900SHA1State),
.instance_init = s5l8900_sha1_init,
.class_init = s5l8900_sha1_class_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_sha1_info);
}
type_init(ipod_touch_machine_types)
+310
View File
@@ -0,0 +1,310 @@
/*
* S5L8900 SPI Emulation
*
* by cmw
*/
#include "hw/arm/ipod_touch_spi.h"
static int apple_spi_word_size(S5L8900SPIState *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(S5L8900SPIState *s)
{
if (fifo8_is_empty(&s->tx_fifo)) {
REG(s, R_STATUS) |= R_STATUS_TXEMPTY;
}
}
static void apple_spi_update_xfer_rx(S5L8900SPIState *s)
{
if (!fifo8_is_empty(&s->rx_fifo)) {
REG(s, R_STATUS) |= R_STATUS_RXREADY;
}
}
static void apple_spi_update_irq(S5L8900SPIState *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(S5L8900SPIState *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)
{
S5L8900SPIState *s = S5L8900SPI(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(S5L8900SPIState *s)
{
uint32_t tx;
uint32_t rx;
if (!(REG(s, R_CTRL) & R_CTRL_RUN)) {
return;
}
while (!fifo8_is_empty(&s->tx_fifo)) {
tx = (uint32_t)fifo8_pop(&s->tx_fifo);
rx = ssi_transfer(s->spi, tx);
apple_spi_update_xfer_tx(s);
if (REG(s, R_RXCNT) > 0) {
if (fifo8_is_full(&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)) {
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 && fifo8_is_empty(&s->tx_fifo)) {
REG(s, R_STATUS) |= R_STATUS_COMPLETE;
REG(s, R_CTRL) &= ~R_CTRL_RUN;
}
}
static uint64_t s5l8900_spi_read(void *opaque, hwaddr addr, unsigned size)
{
S5L8900SPIState *s = S5L8900SPI(opaque);
//fprintf(stderr, "%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)) {
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;
val &= (R_STATUS_TXFIFO_MASK | R_STATUS_RXFIFO_MASK);
r &= ~(R_STATUS_TXFIFO_MASK | R_STATUS_RXFIFO_MASK);
r |= val;
break;
}
default:
break;
}
if (run) {
apple_spi_run(s);
}
apple_spi_update_irq(s);
return r;
}
static void s5l8900_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
{
S5L8900SPIState *s = S5L8900SPI(opaque);
//fprintf(stderr, "%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:
r = old & (~r);
run = true;
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("OVERFLOW: %d\n", fifo8_num_free(&s->tx_fifo));
qemu_log_mask(LOG_GUEST_ERROR, "%s: tx overflow\n", __func__);
r = 0;
break;
}
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);
}
apple_spi_update_irq(s);
}
static const MemoryRegionOps spi_ops = {
.read = s5l8900_spi_read,
.write = s5l8900_spi_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void s5l8900_spi_reset(DeviceState *d)
{
S5L8900SPIState *s = (S5L8900SPIState *)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 s5l8900_spi_realize(DeviceState *dev, struct Error **errp)
{
S5L8900SPIState *s = S5L8900SPI(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_DEPTH);
fifo8_create(&s->rx_fifo, R_FIFO_DEPTH);
// create the peripheral
switch(s->base) {
case 0:
break;
case 1:
ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_LCD_PANEL);
break;
case 2:
{
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_MULTITOUCH);
IPodTouchMultitouchState *mt = IPOD_TOUCH_MULTITOUCH(dev);
s->mt = mt;
break;
}
}
}
static void s5l8900_spi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = s5l8900_spi_realize;
dc->reset = s5l8900_spi_reset;
}
static const TypeInfo s5l8900_spi_info = {
.name = TYPE_S5L8900SPI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(S5L8900SPIState),
.class_init = s5l8900_spi_class_init,
};
static void s5l8900_spi_register_types(void)
{
type_register_static(&s5l8900_spi_info);
}
type_init(s5l8900_spi_register_types)
+127
View File
@@ -0,0 +1,127 @@
#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 (3 << 24); //for older iboots
return (2 << 0x18);
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_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)
+68
View File
@@ -0,0 +1,68 @@
#include "hw/arm/ipod_touch_tvout.h"
#include "qapi/error.h"
static uint64_t ipod_touch_tvout_read(void *opaque, hwaddr offset, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//fprintf(stderr, "%s (%d): offset = 0x%08x\n", __func__, s->index, offset);
// switch(offset) {
// case SDO_IRQ:
// return s->sdo_irq_reg;
// }
return s->data[offset];
}
static void ipod_touch_tvout_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
//fprintf(stderr, "%s (%d): writing 0x%08x to 0x%08x\n", __func__, s->index, value, offset);
s->data[offset] = value;
// switch(offset) {
// case SDO_IRQ:
// s->sdo_irq_reg = value;
// break;
// default:
// break;
// }
}
static const MemoryRegionOps ipod_touch_tvout_ops = {
.read = ipod_touch_tvout_read,
.write = ipod_touch_tvout_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);
memory_region_init_io(&s->iomem, obj, &ipod_touch_tvout_ops, s, "tvout", 4096);
sysbus_init_mmio(sbd, &s->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)
+617
View File
@@ -0,0 +1,617 @@
/*
* 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 "qemu-common.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)
+1
View File
@@ -58,5 +58,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', if_true: files('ipod_touch.c', 'ipod_touch_spi.c', 'ipod_touch_sysic.c', 'ipod_touch_aes.c', 'ipod_touch_sha1.c', 'ipod_touch_usb_otg.c', 'ipod_touch_8900_engine.c', 'ipod_touch_nand.c', 'ipod_touch_nand_ecc.c', 'ipod_touch_pcf50633_pmu.c', 'ipod_touch_adm.c', 'ipod_touch_chipid.c', 'ipod_touch_tvout.c', 'ipod_touch_lcd_panel.c', 'ipod_touch_multitouch.c', 'ipod_touch_lcd.c', 'ipod_touch_lis302dl.c', 'ipod_touch_aes.c', 'ipod_touch_sha1.c', 'ipod_touch_timer.c', 'ipod_touch_clock.c', 'ipod_touch_gpio.c', 'ipod_touch_sdio.c'))
hw_arch += {'arm': arm_ss}
+17 -5
View File
@@ -83,6 +83,8 @@ static void pl080_update(PL080State *s)
bool tclevel = (s->tc_int & s->tc_mask);
bool errlevel = (s->err_int & s->err_mask);
//printf("(DMA) Setting irq: %d\n", errlevel || tclevel);
qemu_set_irq(s->interr, errlevel);
qemu_set_irq(s->inttc, tclevel);
qemu_set_irq(s->irq, errlevel || tclevel);
@@ -142,12 +144,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 +157,9 @@ again:
size = 0;
break;
}
if (!size)
if (!size) {
continue;
}
/* Transfer one element. */
/* ??? Should transfer multiple elements for a burst request. */
@@ -179,6 +182,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 +208,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 +217,7 @@ again:
if (--s->running)
s->running = 1;
}
pl080_update(s);
}
static uint64_t pl080_read(void *opaque, hwaddr offset,
@@ -289,24 +295,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;
+237
View File
@@ -0,0 +1,237 @@
/*
* 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;
//fprintf(stderr, "s5l8900_i2c_write: offset = 0x%08x, val = 0x%08x\n", 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 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);
s->bus = i2c_init_bus(dev, "i2c");
}
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'))
arm_ss.add(when: 'CONFIG_IPOD_TOUCH', 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)
+4
View File
@@ -16,6 +16,7 @@ typedef struct aes_key_st AES_KEY;
#define AES_set_decrypt_key QEMU_AES_set_decrypt_key
#define AES_encrypt QEMU_AES_encrypt
#define AES_decrypt QEMU_AES_decrypt
#define AES_cbc_encrypt QEMU_AES_cbc_encrypt
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
AES_KEY *key);
@@ -26,6 +27,9 @@ void AES_encrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key);
void AES_decrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key);
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
const unsigned long length, const AES_KEY *key,
unsigned char *ivec, const int enc);
extern const uint8_t AES_sbox[256];
extern const uint8_t AES_isbox[256];
+175
View File
@@ -0,0 +1,175 @@
#ifndef HW_ARM_IPOD_TOUCH_H
#define HW_ARM_IPOD_TOUCH_H
#include "qemu-common.h"
#include "exec/hwaddr.h"
#include "ui/console.h"
#include "hw/boards.h"
#include "hw/arm/boot.h"
#include "hw/intc/pl192.h"
#include "hw/arm/ipod_touch_timer.h"
#include "hw/arm/ipod_touch_clock.h"
#include "hw/arm/ipod_touch_spi.h"
#include "hw/arm/ipod_touch_aes.h"
#include "hw/arm/ipod_touch_sha1.h"
#include "hw/arm/ipod_touch_8900_engine.h"
#include "hw/arm/ipod_touch_usb_otg.h"
#include "hw/arm/ipod_touch_nand.h"
#include "hw/arm/ipod_touch_nand_ecc.h"
#include "hw/arm/ipod_touch_pcf50633_pmu.h"
#include "hw/arm/ipod_touch_adm.h"
#include "hw/arm/ipod_touch_sysic.h"
#include "hw/arm/ipod_touch_chipid.h"
#include "hw/arm/ipod_touch_tvout.h"
#include "hw/arm/ipod_touch_lcd.h"
#include "hw/arm/ipod_touch_gpio.h"
#include "hw/arm/ipod_touch_sdio.h"
#include "hw/i2c/ipod_touch_i2c.h"
#include "cpu.h"
#define TYPE_IPOD_TOUCH "iPod-Touch"
#define TYPE_IPOD_TOUCH_MACHINE MACHINE_TYPE_NAME(TYPE_IPOD_TOUCH)
#define IPOD_TOUCH_MACHINE(obj) \
OBJECT_CHECK(IPodTouchMachineState, (obj), TYPE_IPOD_TOUCH_MACHINE)
// VIC
#define S5L8900_VIC_N 2
#define S5L8900_VIC_SIZE 32
#define S5L8900_TIMER1_IRQ 0x7
#define S5L8900_SPI0_IRQ 0x9
#define S5L8900_SPI1_IRQ 0xA
#define S5L8900_SPI2_IRQ 0xB
#define S5L8900_LCD_IRQ 0xD
#define S5L8900_DMAC0_IRQ 0x10
#define S5L8900_DMAC1_IRQ 0x11
#define S5L8900_I2C0_IRQ 0x15
#define S5L8900_I2C1_IRQ 0x16
#define S5L8900_ADM_IRQ 0x25
#define S5L8900_NAND_ECC_IRQ 0x2B
#define S5L8900_TVOUT_SDO_IRQ 0x1E
#define S5L8900_TVOUT_MIXER_IRQ 0x26
// 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
#define S5L8900_GPIO_G5_IRQ 0x01
#define S5L8900_GPIO_G6_IRQ 0x00
const int S5L8900_GPIO_IRQS[7] = { S5L8900_GPIO_G0_IRQ, S5L8900_GPIO_G1_IRQ, S5L8900_GPIO_G2_IRQ, S5L8900_GPIO_G3_IRQ, S5L8900_GPIO_G4_IRQ, S5L8900_GPIO_G5_IRQ, S5L8900_GPIO_G6_IRQ };
// memory addresses
#define IPOD_TOUCH_PHYS_BASE (0xc0000000)
#define IBOOT_BASE 0x18000000
#define LLB_BASE 0x22000000
#define SRAM1_MEM_BASE 0x22020000
#define CLOCK0_MEM_BASE 0x38100000
#define CLOCK1_MEM_BASE 0x3C500000
#define SYSIC_MEM_BASE 0x39A00000
#define VIC0_MEM_BASE 0x38E00000
#define VIC1_MEM_BASE 0x38E01000
#define EDGEIC_MEM_BASE 0x38E02000
#define IIS0_MEM_BASE 0x3D400000
#define IIS1_MEM_BASE 0x3CD00000
#define IIS2_MEM_BASE 0x3CA00000
#define NOR_MEM_BASE 0x24000000
#define TIMER1_MEM_BASE 0x3E200000
#define USBOTG_MEM_BASE 0x38400000
#define USBPHYS_MEM_BASE 0x3C400000
#define GPIO_MEM_BASE 0x3E400000
#define I2C0_MEM_BASE 0x3C600000
#define I2C1_MEM_BASE 0x3C900000
#define SPI0_MEM_BASE 0x3C300000
#define SPI1_MEM_BASE 0x3CE00000
#define SPI2_MEM_BASE 0x3D200000
#define WATCHDOG_MEM_BASE 0x3E300000
#define DISPLAY_MEM_BASE 0x38900000
#define CHIPID_MEM_BASE 0x3e500000
#define RAM_MEM_BASE 0x8000000
#define SHA1_MEM_BASE 0x38000000
#define AES_MEM_BASE 0x38C00000
#define VROM_MEM_BASE 0x20000000
#define NAND_MEM_BASE 0x38A00000
#define NAND_ECC_MEM_BASE 0x38F00000
#define DMAC0_MEM_BASE 0x38200000
#define DMAC1_MEM_BASE 0x39900000
#define UART0_MEM_BASE 0x3CC00000
#define UART1_MEM_BASE 0x3CC04000
#define UART2_MEM_BASE 0x3CC08000
#define UART3_MEM_BASE 0x3CC0C000
#define UART4_MEM_BASE 0x3CC10000
#define ADM_MEM_BASE 0x38800000
#define MBX_MEM_BASE 0x3B000000
#define ENGINE_8900_MEM_BASE 0x3F000000
#define KERNELCACHE_BASE 0x9000000
#define SDIO_MEM_BASE 0x38d00000
#define FRAMEBUFFER_MEM_BASE 0xfe00000
#define TVOUT1_MEM_BASE 0x39100000
#define TVOUT2_MEM_BASE 0x39200000
#define TVOUT3_MEM_BASE 0x39300000
#define MPVD_MEM_BASE 0x39600000
#define H264BPD_MEM_BASE 0x39800000
#define TVOUT_WORKAROUND_MEM_BASE 0x8a25960 // workaround for TV Out, to make sure that it can be correctly deallocated without reverse engineering the entire TVOut protocol
typedef struct {
MachineClass parent;
} IPodTouchMachineClass;
typedef struct s5l8900_usb_phys_s
{
uint32_t usb_ophypwr;
uint32_t usb_ophyclk;
uint32_t usb_orstcon;
uint32_t usb_ophytune;
} s5l8900_usb_phys_s;
typedef struct {
MachineState parent;
qemu_irq **irq;
PL192State *vic0;
PL192State *vic1;
synopsys_usb_state *usb_otg;
IPodTouchClockState *clock0;
IPodTouchClockState *clock1;
IPodTouchTimerState *timer1;
IPodTouchSYSICState *sysic;
S5L8900SPIState *spi2_state;
s5l8900_usb_phys_s *usb_phys;
S5L8900AESState *aes_state;
S5L8900SHA1State *sha1_state;
ITNandState *nand_state;
ITNandECCState *nand_ecc_state;
IPodTouchI2CState *i2c0_state;
IPodTouchI2CState *i2c1_state;
IPodTouchADMState *adm_state;
IPodTouchChipIDState *chipid_state;
IPodTouchTVOutState *tvout1_state;
IPodTouchTVOutState *tvout2_state;
IPodTouchTVOutState *tvout3_state;
IPodTouchLCDState *lcd_state;
IPodTouchGPIOState *gpio_state;
IPodTouchSDIOState *sdio_state;
Clock *sysclk;
uint32_t kpc_pa;
uint32_t kbootargs_pa;
uint32_t uart_mmio_pa;
ARMCPU *cpu;
char bootrom_path[1024];
char iboot_path[1024];
char nand_path[1024];
} IPodTouchMachineState;
#endif
+112
View File
@@ -0,0 +1,112 @@
#ifndef HW_ARM_IPOD_TOUCH_8900_ENGINE_H
#define HW_ARM_IPOD_TOUCH_8900_ENGINE_H
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/hw.h"
#include "exec/hwaddr.h"
#include "exec/memory.h"
#include <openssl/aes.h>
typedef struct {
unsigned char magic[ 4 ];
unsigned char version[ 3 ];
unsigned char encrypted;
unsigned char unknownNull[4];
unsigned int sizeOfData;
unsigned int footerSignatureOffset;
unsigned int footerCertOffset;
unsigned int footerCertLen;
unsigned char key1[ 32 ];
unsigned char unknownVersion[ 4 ];
unsigned char key2[ 16 ];
unsigned char padding[ 1968 ];
} header8900;
static uint64_t s5l8900_8900_engine_read(void *opaque, hwaddr offset, unsigned size)
{
return 0;
}
static void convert_hex_key_to_bin(char *str, uint8_t *bytes, int maxlen)
{
int slen = strlen(str);
int bytelen = maxlen;
int rpos, wpos = 0;
for(rpos = 0; rpos < bytelen; rpos++) {
sscanf(&str[rpos*2], "%02hhx", &bytes[wpos++]);
}
}
static void s5l8900_8900_engine_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
AES_KEY ctx, aes_decrypt_key;
uint8_t aes_key[16];
unsigned char ramdiskKey[32] = "188458A6D15034DFE386F23B61D43774";
unsigned char ramdiskiv[1];
unsigned char keybuf[16];
unsigned char iv[AES_BLOCK_SIZE];
int encrypted;
off_t data_begin, data_current, data_end, data_len;
if(offset != 0x0) { return; }
printf("Reading 8900 header with length %d at address 0x%08x\n", sizeof(header8900), value);
AddressSpace *nsas = (AddressSpace *)opaque;
header8900 *header = malloc(sizeof(header8900));
address_space_rw(nsas, value, MEMTXATTRS_UNSPECIFIED, (uint8_t *)header, sizeof(header8900), 0);
if( (header->magic[0] != 0x38) && // 8
(header->magic[1] != 0x39) && // 9
(header->magic[2] != 0x30) && // 0
(header->magic[3] != 0x30)) // 0
{
printf("Bad 8900 magic\n");
return;
}
if(header->encrypted == 0x03) { encrypted = 1; }
else if( header->encrypted = 0x04 ) { encrypted = 0; }
data_begin = sizeof(header8900);
data_len = header->sizeOfData;
printf("Will decrypt 8900 image at address 0x%08x (len: %d bytes)\n", value, data_len);
// read the data into a buffer
uint8_t *inbuf = (uint8_t *)malloc(data_len);
address_space_rw(nsas, value + sizeof(header8900), MEMTXATTRS_UNSPECIFIED, (uint8_t *)inbuf, data_len, 0);
uint8_t *outbuf = (uint8_t *)malloc(data_len);
uint8_t *intbuf = (uint8_t *)malloc(AES_BLOCK_SIZE);
convert_hex_key_to_bin(ramdiskKey, aes_key, 16);
AES_set_decrypt_key(aes_key, 128, &aes_decrypt_key);
memset(iv, 0, AES_BLOCK_SIZE);
data_current = 0;
while(data_current < data_len)
{
AES_cbc_encrypt(inbuf + data_current, intbuf, AES_BLOCK_SIZE, &aes_decrypt_key, iv, 0);
memcpy(outbuf + data_current, intbuf, AES_BLOCK_SIZE);
data_current = data_current + AES_BLOCK_SIZE;
}
// write the decrypted output buffer
address_space_rw(nsas, value + sizeof(header8900), MEMTXATTRS_UNSPECIFIED, (uint8_t *)outbuf, data_len, 1);
free(header);
free(inbuf);
free(outbuf);
}
static const MemoryRegionOps engine_8900_ops = {
.read = s5l8900_8900_engine_read,
.write = s5l8900_8900_engine_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
#endif
+38
View File
@@ -0,0 +1,38 @@
#ifndef IPOD_TOUCH_ADM_H
#define IPOD_TOUCH_ADM_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_nand.h"
#define TYPE_IPOD_TOUCH_ADM "ipodtouch.adm"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchADMState, IPOD_TOUCH_ADM)
#define ADM_CTRL 0x0
#define ADM_CTRL2 0x4
#define ADM_CODE_SEC_ADDR 0x50
#define ADM_DATA1_SEC_ADDR 0x84
#define ADM_DATA2_SEC_ADDR 0x88
#define ADM_DATA3_SEC_ADDR 0x8C
typedef struct IPodTouchADMState {
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t code_sec_addr;
uint32_t data1_sec_addr;
uint32_t data2_sec_addr;
uint32_t data3_sec_addr;
MemoryRegion *downstream;
AddressSpace downstream_as;
ITNandState *nand_state;
} IPodTouchADMState;
#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 "qemu-common.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(S5L8900AESState, 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 S5L8900AESState
{
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];
} S5L8900AESState;
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef HW_ARM_IPOD_TOUCH_CHIPID_H
#define HW_ARM_IPOD_TOUCH_CHIPID_H
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/sysbus.h"
#define TYPE_IPOD_TOUCH_CHIPID "ipodtouch.chipid"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchChipIDState, IPOD_TOUCH_CHIPID)
#define CHIP_REVISION 0x2
typedef struct IPodTouchChipIDState {
SysBusDevice busdev;
MemoryRegion iomem;
} IPodTouchChipIDState;
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef IPOD_TOUCH_CLOCK_H
#define IPOD_TOUCH_CLOCK_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_CLOCK "ipodtouch.clock"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchClockState, IPOD_TOUCH_CLOCK)
#define CLOCK1_CONFIG0 0x0
#define CLOCK1_CONFIG1 0x4
#define CLOCK1_CONFIG2 0x8
#define CLOCK1_PLL0CON 0x20
#define CLOCK1_PLL1CON 0x24
#define CLOCK1_PLL2CON 0x28
#define CLOCK1_PLL3CON 0x2C
#define CLOCK1_PLLLOCK 0x40
#define CLOCK1_PLLMODE 0x44
typedef struct IPodTouchClockState
{
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t config0;
uint32_t config1;
uint32_t config2;
uint32_t pll0con;
uint32_t pll1con;
uint32_t pll2con;
uint32_t pll3con;
uint32_t plllock;
} IPodTouchClockState;
#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 0x1605
#define GPIO_BUTTON_HOME 0x1606
#define GPIO_BUTTON_POWER_IRQ 0x2D
#define GPIO_BUTTON_HOME_IRQ 0x2E
#define NUM_GPIO_PINS 0x20
typedef struct IPodTouchGPIOState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t gpio_state;
} IPodTouchGPIOState;
#endif
+57
View File
@@ -0,0 +1,57 @@
#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 lcd_con2;
uint32_t unknown1;
uint32_t unknown2;
uint32_t wnd_con;
uint32_t vid_con0;
uint32_t vid_con1;
uint32_t vidt_con0;
uint32_t vidt_con1;
uint32_t vidt_con2;
uint32_t vidt_con3;
uint32_t w1_hspan;
uint32_t w1_framebuffer_base;
uint32_t w1_display_resolution_info;
uint32_t w1_display_depth_info;
uint32_t w1_qlen;
uint32_t w2_hspan;
uint32_t w2_framebuffer_base;
uint32_t w2_display_resolution_info;
uint32_t w2_display_depth_info;
uint32_t w2_qlen;
QEMUTimer *refresh_timer;
} IPodTouchLCDState;
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef IPOD_TOUCH_LCD_PANEL_H
#define IPOD_TOUCH_LCD_PANEL_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/ssi/ssi.h"
#define TYPE_IPOD_TOUCH_LCD_PANEL "ipodtouch.lcdpanel"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchLCDPanelState, IPOD_TOUCH_LCD_PANEL)
typedef struct IPodTouchLCDPanelState {
SSIPeripheral ssidev;
uint32_t cur_cmd;
} IPodTouchLCDPanelState;
#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
+161
View File
@@ -0,0 +1,161 @@
#ifndef IPOD_TOUCH_MULTITOUCH_H
#define IPOD_TOUCH_MULTITOUCH_H
#include "qemu/osdep.h"
#include "qemu-common.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
+67
View File
@@ -0,0 +1,67 @@
#ifndef HW_ARM_IPOD_TOUCH_NAND_H
#define HW_ARM_IPOD_TOUCH_NAND_H
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "hw/irq.h"
#include "qemu/lockable.h"
#define NAND_NUM_BANKS 8
#define NAND_BYTES_PER_PAGE 2048
#define NAND_BYTES_PER_SPARE 64
#define NAND_CHIP_ID 0xA514D3AD
#define NAND_FMCTRL0 0x0
#define NAND_FMCTRL1 0x4
#define NAND_CMD 0x8
#define NAND_FMADDR0 0xC
#define NAND_FMADDR1 0x10
#define NAND_FMANUM 0x2C
#define NAND_FMDNUM 0x30
#define NAND_FMCSTAT 0x48
#define NAND_FMFIFO 0x80
#define NAND_RSCTRL 0x100
#define NAND_CMD_ID 0x90
#define NAND_CMD_READ 0x30
#define NAND_CMD_READSTATUS 0x70
#define FILESYSTEM_START_VPN 206851
#define FILESYSTEM_NUM_PAGES 132854
#define TYPE_ITNAND "itnand"
OBJECT_DECLARE_SIMPLE_TYPE(ITNandState, ITNAND)
typedef struct ITNandState {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t fmctrl0;
uint32_t fmctrl1;
uint32_t fmaddr0;
uint32_t fmaddr1;
uint32_t fmanum;
uint32_t fmdnum;
uint32_t rsctrl;
uint32_t cmd;
uint8_t reading_spare;
qemu_irq irq;
uint8_t *page_buffer;
uint8_t *page_spare_buffer;
uint32_t buffered_bank;
uint32_t buffered_page;
bool reading_multiple_pages;
uint32_t cur_bank_reading;
uint32_t banks_to_read[512]; // used when in multiple page read mode
uint32_t pages_to_read[512]; // used when in multiple page read mode
bool is_writing;
QemuMutex lock;
char *nand_path;
} ITNandState;
void nand_set_buffered_page(ITNandState *s, uint32_t page);
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef HW_ARM_IPOD_TOUCH_NAND_ECC_H
#define HW_ARM_IPOD_TOUCH_NAND_ECC_H
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "hw/platform-bus.h"
#include "hw/hw.h"
#include "hw/irq.h"
#define NANDECC_DATA 0x4
#define NANDECC_ECC 0x8
#define NANDECC_START 0xC
#define NANDECC_STATUS 0x10
#define NANDECC_SETUP 0x14
#define NANDECC_CLEARINT 0x40
#define TYPE_ITNANDECC "itnand_ecc"
OBJECT_DECLARE_SIMPLE_TYPE(ITNandECCState, ITNANDECC)
typedef struct ITNandECCState {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t data_addr;
uint32_t ecc_addr;
uint32_t status;
uint32_t setup;
qemu_irq irq;
} ITNandECCState;
#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
+37
View File
@@ -0,0 +1,37 @@
#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"
#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_DSTA 0x18
#define SDIO_RESP0 0x20
#define SDIO_RESP1 0x24
#define SDIO_RESP2 0x28
#define SDIO_RESP3 0x2C
#define SDIO_CSR 0x34
#define SDIO_IRQMASK 0x3C
typedef struct IPodTouchSDIOState
{
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t cmd;
uint32_t arg;
uint32_t csr;
uint32_t resp0;
uint32_t resp1;
uint32_t resp2;
uint32_t resp3;
uint32_t irq_mask;
} IPodTouchSDIOState;
#endif
+39
View File
@@ -0,0 +1,39 @@
#ifndef HW_ARM_IPOD_TOUCH_SHA1_H
#define HW_ARM_IPOD_TOUCH_SHA1_H
#include "qemu/osdep.h"
#include "qemu-common.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(S5L8900SHA1State, IPOD_TOUCH_SHA1)
#define SHA1_BUFFER_SIZE 1024 * 1024
#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 S5L8900SHA1State {
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;
} S5L8900SHA1State;
#endif
+86
View File
@@ -0,0 +1,86 @@
#ifndef HW_ARM_IPOD_TOUCH_SPI_H
#define HW_ARM_IPOD_TOUCH_SPI_H
#include "qemu/osdep.h"
#include "qemu-common.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_lcd_panel.h"
#include "hw/arm/ipod_touch_multitouch.h"
#define TYPE_S5L8900SPI "s5l8900spi"
OBJECT_DECLARE_SIMPLE_TYPE(S5L8900SPIState, S5L8900SPI)
#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_MODE(_x) (((_x) >> 5) & 0x3)
#define R_CFG_MODE_POLLED 0
#define R_CFG_MODE_IRQ 1
#define R_CFG_MODE_DMA 2
#define R_CFG_IE_RXREADY (1 << 7)
#define R_CFG_IE_TXEMPTY (1 << 8)
#define R_CFG_WORD_SIZE(_x) (((_x) >> 13) & 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 (4)
#define R_STATUS_TXFIFO_MASK (31 << R_STATUS_TXFIFO_SHIFT)
#define R_STATUS_RXFIFO_SHIFT (8)
#define R_STATUS_RXFIFO_MASK (31 << R_STATUS_RXFIFO_SHIFT)
#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_DEPTH 50000
#define REG(_s,_v) ((_s)->regs[(_v)>>2])
#define MMIO_SIZE (0x4000)
typedef struct S5L8900SPIState {
SysBusDevice parent_obj;
MemoryRegion iomem;
SSIBus *spi;
IPodTouchMultitouchState *mt;
qemu_irq irq;
uint32_t last_irq;
qemu_irq cs_line;
Fifo8 rx_fifo;
Fifo8 tx_fifo;
uint32_t regs[MMIO_SIZE >> 2];
uint32_t mmio_size;
uint8_t base;
} S5L8900SPIState;
void set_spi_base(uint32_t base);
#endif
+42
View File
@@ -0,0 +1,42 @@
#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_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 0xF8
#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
+26
View File
@@ -0,0 +1,26 @@
#ifndef IPOD_TOUCH_TVOUT_H
#define IPOD_TOUCH_TVOUT_H
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#define SDO_IRQ 0x280
#define TYPE_IPOD_TOUCH_TVOUT "ipodtouch.tvout"
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTVOutState, IPOD_TOUCH_TVOUT)
typedef struct IPodTouchTVOutState {
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t index;
//uint32_t sdo_irq_reg;
uint32_t data[4096];
} IPodTouchTVOutState;
#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
+64
View File
@@ -0,0 +1,64 @@
#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 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
+4 -4
View File
@@ -249,7 +249,7 @@ static void absolute_mouse_grab(struct sdl2_console *scon)
SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
if (mouse_x > 0 && mouse_x < scr_w - 1 &&
mouse_y > 0 && mouse_y < scr_h - 1) {
sdl_grab_start(scon);
//sdl_grab_start(scon);
}
}
@@ -486,7 +486,7 @@ static void handle_mousemotion(SDL_Event *ev)
if (!gui_grab &&
(ev->motion.x > 0 && ev->motion.x < max_x &&
ev->motion.y > 0 && ev->motion.y < max_y)) {
sdl_grab_start(scon);
//sdl_grab_start(scon);
}
}
if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
@@ -509,7 +509,7 @@ static void handle_mousebutton(SDL_Event *ev)
if (!gui_grab && !qemu_input_is_absolute()) {
if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
/* start grabbing all events */
sdl_grab_start(scon);
//sdl_grab_start(scon);
}
} else {
if (ev->type == SDL_MOUSEBUTTONDOWN) {
@@ -911,7 +911,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
sdl_cursor_normal = SDL_GetCursor();
if (gui_fullscreen) {
sdl_grab_start(&sdl2_console[0]);
//sdl_grab_start(&sdl2_console[0]);
}
atexit(sdl_cleanup);